2012-02-23 102 views
3

我在寫一個合併兩個列表的函數。如果列表中的一個項目是列表,我也需要提取它。合併和提取列表

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) 

我試圖通過我的代碼解決它不起作用。問題是什麼?

(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) '()))) 

回答

10

最簡單的方法是簡單地使用庫函數flatten

(define (merge lis1 lis2) 
    (flatten (cons lis1 lis2))) 

flatten需要,可以包含列表(誰又可以包含多個列表,...)的列表,並將結果合併爲未列出的清單,這是你的組合功能似乎什麼是試圖去做。

(flatten '(1 2 (3 (4 5) 6)))回報'(1 2 3 4 5 6)

如果這個庫的功能是關閉的限制,你的代碼實際上是相當接近正確。

第一個問題是在((list? lis) (combine (car lis) fine) (combine (cdr lis) fine) )行。 fine永不改變,因此代碼評估(combine (car lis) fine),然後返回(combine (cdr lis) fine),其中第二個表達式中的fine是原始值fine。這條線與((list? lis) (combine (cdr lis) fine) )相同,顯然不是我們想要的。相反,我們必須使用第二個表達式((list? lis) (combine (cdr lis) (combine (car lis) fine)))中的第一個表達式。

第二個問題是,在組合中,當lis爲空時,我們需要返回fine而不是lis

下一個問題是,這段代碼遍歷列表,將lis的第一個元素放在fine的前面,然後傳遞新創建的列表並將其用於下一次函數迭代,其中它在lis中取第二個值,並將其粘貼在新的fine的前面,在lis的第一個值前面。返回值爲fine的結果順序顛倒 - (merge '(1 2 (3)) '(4 (5 6)))將返回(6 5 4 3 2 1)。我們有兩種選擇:我們可以在merge正文中從combine返回,或者我們可以反轉我們上面更改的行,使其成爲((list? lis) (combine (car lis) (combine (cdr lis) fine)))。這意味着在添加當前元素之前,我們會將列表的其餘部分添加到fine,這正是我們想要的。

另一個問題是我們需要將lis1改爲lis2,而不是相反。

最後一個問題是merge正文中的cond是不必要的 - 我們可以將其刪除。

作爲一個方面說明,通常認爲整齊的方法是不要給圓括號一個新的行,並將definecond的主體縮進兩個空格。

所有這些變化,最終的代碼是:

(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) '()))