合并和提取列表

时间:2012-02-23 04:55:40

标签: lisp scheme

我正在编写一个合并两个列表的函数。如果列表中的一个项目是列表,我也需要提取它。

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

1 个答案:

答案 0 :(得分: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),其中第二个表达式中的finefine的原始值。这一行与((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)。我们有两个选择:我们可以在combine正文中从merge返回反向调用,或者我们可以反转上面更改的行,使其成为((list? lis) (combine (car lis) (combine (cdr lis) fine)))。这意味着我们会在添加当前元素之前将列表的其余部分添加到fine,这就是我们想要的。

另一个问题是我们需要lis1lis2,而不是相反。

最后一个问题是,cond正文中的merge是不必要的 - 我们可以删除它。

作为旁注,通常认为不要给近侧括号添加新行并将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) '()))