在Racket中实现通话记录按号码汇总为联系人时长列表
Let's work through your Racket problem step by step! First we'll fix the bug in your filter-by-number function, then build a solid summarize-contacts implementation to aggregate call durations by phone number.
1. Fixing the filter-by-number Function
Your current code has two critical issues that cause it to fail:
- When a matching call is found, you're recursively calling
filter-by-numberon(first lst)(a single call struct, not a list), which triggers a type error. - In the non-matching branch, you return
emptyimmediately instead of continuing to check the rest of the list, which discards all subsequent calls.
Here's the corrected version with explanations:
; (list-of-Call) String -> (List-of-Call) ; Filters a list of calls to only those with the given phone number (check-expect (filter-by-number empty "101010") empty) (check-expect (filter-by-number (list CALL1 CALL1) "101010") (list CALL1 CALL1)) (check-expect (filter-by-number (list CALL1 CALL2) "Jonas") empty) (check-expect (filter-by-number (list CALL1 CALL2 CALL1) "101010") (list CALL1 CALL1)) ; Added test for mixed list (define (filter-by-number lst target-num) (cond [(empty? lst) empty] [(string=? (call-callee (first lst)) target-num) ; Keep the matching call, then recurse on the rest of the list (cons (first lst) (filter-by-number (rest lst) target-num))] [else ; Skip this call, keep checking the rest of the list (filter-by-number (rest lst) target-num)]))
We switched to cond for better readability, use cons to efficiently build the result list, and ensure the non-matching branch continues processing instead of cutting off early.
2. Building the summarize-contacts Function
The core logic here is to:
- Extract all unique phone numbers from the call list
- For each unique number, calculate the total duration of all calls to it
- Convert each number + total duration into a
contactstruct
First, let's add a helper function to sum call durations:
; (List-of-Call) -> Number ; Sums the durations of all calls in the list (define (total-duration calls) (foldr (lambda (call acc) (+ (call-duration call) acc)) 0 calls))
Then the main summarize-contacts function:
; (List-of-Call) -> (List-of-Contact) ; Aggregates calls by phone number, creating a contact for each number with total duration (check-expect (summarize-contacts empty) empty) (check-expect (summarize-contacts (list CALL1)) (list (make-contact "101010" 4.5))) (check-expect (summarize-contacts (list CALL1 CALL2 CALL3)) (list (make-contact "101010" 4.5) (make-contact "222222" 1.0) (make-contact "123456" 20.2))) (check-expect (summarize-contacts (list CALL4 CALL4)) (list (make-contact "999999" 20))) ; Fixed your broken test case (original had syntax errors) (check-expect (summarize-contacts (list CALL4 CALL4 CALL1)) (list (make-contact "999999" 20) (make-contact "101010" 4.5))) (define (summarize-contacts lst) (let* ([all-numbers (map call-callee lst)] ; Extract all phone numbers from calls [unique-numbers (remove-duplicates all-numbers string=?)]) ; Get unique numbers (using string equality) ; For each unique number, create a contact with its total call duration (map (lambda (num) (make-contact num (total-duration (filter-by-number lst num)))) unique-numbers)))
Key Details:
- We use
mapto pull all phone numbers from the call list, thenremove-duplicateswithstring=?to avoid processing duplicate numbers. - The final
mapconverts each unique number into a contact: we filter the original list to get all calls for that number, then sum their durations with our helper function.
3. Optional: More Efficient Alternative
If you're working with large call lists, the above approach is simple but not the most efficient (it filters the entire list once per unique number). A faster single-pass method uses a hash table to accumulate durations:
(define (summarize-contacts-efficient lst) (let ([duration-hash (make-hash)]) ; Iterate through all calls, updating the hash table with total durations (for-each (lambda (call) (let ([num (call-callee call)] [dur (call-duration call)]) (hash-update! duration-hash num (lambda (current) (+ current dur)) 0))) lst) ; Convert the hash table entries into a list of contacts (hash-map duration-hash (lambda (num total) (make-contact num total)))))
This version runs in O(n) time (vs O(n*m) for the first approach, where m is the number of unique numbers) and is ideal for large datasets.
内容的提问来源于stack exchange,提问作者Illmatic

