我正在尝试在Scheme中定义一个函数,使用Pretty Big语言(在Dr. Racket中),它将获取一个列表并将所有'原子'转换为顶级元素。例如,如果给出:
(level '(a b (c d) (e f (g 4 h))))
;=> (a b c d e f g 4 h)
这是我到目前为止的代码:
;;level -takes list and returns list w/all elements as top-level
(define level
(lambda (L)
(cond ((null? L) L)
((not( pair? L)) L)
(else (append (level(car L)) (level(cdr L)))))))
我的错误如下:
append: contract violation
expected: list?
given: d
任何人都可以帮我解决此错误吗?
答案 0 :(得分:2)
有关如何实现flatten
的更多信息(这通常称为此类函数),请查看
至于你的具体错误,append
期望所有(通常可能需要两个以上)的参数成为列表。例如,
> (append '(1 2 3) '(4 5 6))
;=> (1 2 3 4 5 6)
> (append '(1 2 3) '(4 5 6) '(7 8 9))
;=> (1 2 3 4 5 6 7 8 9)
现在,你正在编写你的函数,并且你已经说level
应该返回一个列表。这意味着如果level
有几个不同的执行路径,则每个路径都需要生成一个列表。那么,让我们来看看你的实现。
(define level
(lambda (L)
(cond ((null? L) L)
((not( pair? L)) L)
(else (append (level(car L)) (level(cdr L)))))))
在这个问题中,你说你正在写一个应该列出一个列表的函数,所以L
可以是两件事之一;它可以是空列表,也可以是一对。目前,您的cond
还有三个案例。
(cond ((null? L) L) ; handle an empty list
((not( pair? L)) L)
(else (append (level(car L)) (level(cdr L))))) ; handle a pair
如果您总是使用列表调用level
,则不需要第二种情况。但是,由于在第三种情况下,您执行调用(level (car L))
,并且您不知道(car L)
是否会成为列表,您似乎 最终使用非列表调用level
。您需要做出决定,例如(level 'a)
是否合法,以及是否应该是合法的。目前,您似乎想让(level 'a)
合法并返回(a)
。那很好,但你应该指定合同。如果这是你想要做的,那么你需要cond
中的第二种情况,但由于(level 'a)
应该返回(a)
,你实际上需要这种情况才能返回(list L)
},而不是L
。
这里的另一个选项,如果你做希望level
是严格的,并且总是需要一个列表作为参数,那么你需要添加一些逻辑来确定是否{ {1}}是一个列表,如果是,则以递归方式在其上调用(car L)
,并使用结果调用level
。一种方法是这样的:
append
答案 1 :(得分:1)
在列表中附加作品。如果您使用列表level
拨打'(1 2 3)
第一次迭代,它将执行(append (level '1) (level (cdr '(2 3)))
。现在' 1没有对,因此将评估为1,这是不列表。这就像调用(append '1 ...)
这是合同违规一样。
修改强>
以下是Pretty Big中flatten
的实现。这是基于Chris Jester-Young的answer for a similar question。它比append
版本效率更高。
(define (flatten lst)
;; helper function that accumulates
(define (reverse-flatten-into x lst)
(if (pair? x)
(foldl reverse-flatten-into lst x)
(cons x lst)))
(reverse (reverse-flatten-into lst '())))
答案 2 :(得分:0)
无论何时定义递归函数,每个子句都应该返回相似类型的对象。在您的情况下,第三个子句中的递归调用需要返回一个列表(供append
使用),但第二个子句返回一个'atom'。因此编译器/运行时会抱怨'预期列表'。
对此的修复是在第二个(list L)
子句中返回cond
。