Reputation: 1112
I'm writing a function that merges two lists. If one of the list's item is a list, I need to extract it too.
list1:'(1 (2 3 4) 5 6))
list2: '(7 (8 9) 10 (11))
output: (1 2 3 4 5 6 7 8 9 10 11)
I tried to solve it by my code doesn't work. What is the problem?
(define (merge lis1 lis2)
(define (combine lis fine)
(cond
((null? lis) lis)
((list? lis) (combine (car lis) fine) (combine (cdr lis) fine))
(else (cons lis fine))))
(cond
(combine (cons lis2 lis1) '())))
Upvotes: 2
Views: 3126
Reputation: 3217
The easiest way is to simply use the library function flatten
.
(define (merge lis1 lis2)
(flatten (cons lis1 lis2)))
flatten
takes a list that can contain lists (who in turn can contain more lists, ...) and flattens the result into a list of non-lists, which is what your combine function seems to be trying to do.
(flatten '(1 2 (3 (4 5) 6)))
returns '(1 2 3 4 5 6)
If this library function is off limits, your code is actually pretty close to correct.
The first issue is on the ((list? lis) (combine (car lis) fine) (combine (cdr lis) fine) )
line. fine
is never changed, so the code evaluates (combine (car lis) fine)
and then returns (combine (cdr lis) fine)
, where the fine
in the second expression is the original value of fine
. This line is the same thing as ((list? lis) (combine (cdr lis) fine) )
, which is obviously not what we want. Instead, we have to use the first expression inside the second expression ((list? lis) (combine (cdr lis) (combine (car lis) fine)))
.
The second issue is that, in combine, when lis
is null, we need to return fine
, not lis
.
The next issue is that this code goes through the list, takes the first element of lis
and puts it on the front of fine
, and then passes the newly created list and uses it for the next iteration of the function, where it takes second value in lis
and sticks it on the front of the new fine
, in front of the first value of lis
. The results in the order of the return value of fine
being reversed -- (merge '(1 2 (3)) '(4 (5 6)))
will return (6 5 4 3 2 1)
. We have two choices: we can call reverse on the return from combine
in the body of merge
or we can invert the line we changed above, making it ((list? lis) (combine (car lis) (combine (cdr lis) fine)))
. This means that we would add the rest of the list to fine
before adding the current element, which is what we want.
Another issue is that we need to cons lis1
to lis2
, not the other way around.
The final issue is that the cond
in the body of merge
is unnecessary -- we can remove it.
As a side note, it is generally considered neater to not give close parentheses a new line and indent the body of a define
or cond
by just two spaces.
With all of these changes, the final code is:
(define (merge lis1 lis2)
(define (combine lis fine)
(cond
((null? lis) fine)
((list? lis) (combine (car lis)
(combine (cdr lis) fine)))
(else (cons lis fine))))
(combine (cons lis1 lis2) '()))
Upvotes: 9