nullException
nullException

Reputation: 1112

merging and extracting lists

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

Answers (1)

Retief
Retief

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

Related Questions