我对函数式编程是全新的,对整个编程而言还是相对较新的。所以我很迷茫,无法理解Scheme的语法。但这是一个非常简单的问题。我正在尝试创建一个函数来递归地填充并打印从数字x到y的列表。在递归和这对我来说是一门新语言之间,我感到很困惑。
(define (gen-list x y)
(if (> start end)
'()
(append '() (gen-list ((+ 1 x) y)) ) ))
如果我要输入(gen-list 1 5),我希望结果是1 2 3 45。这在尝试调用自身时会给我一个错误“应用程序:不是过程”再次。我已经解决了该错误,但是无法像我想要的那样远程打印任何内容。任何帮助表示赞赏。
答案 0 :(得分:4)
您有几个错误:
x
和y
,但您将它们称为start
和end
(我建议使用start
和{ {1}}相反,它们使代码更易于理解。)end
包围所有表达式。()
递归构建新列表,cons
用于连接现有列表。append
(即递归中的当前元素)来构建新列表-您只是start
创建空列表。 append
到另一个列表或空列表cons
的元素。这就是为什么我们在基本情况下返回'()
的原因。例如,这是一个包含两个元素的列表:'()
。总而言之,这是构建列表的正确方法:
(cons 1 (cons 2 '()))
作为最后的评论:以上过程已经存在于Racket中,您不需要重写它。在documentation中了解(define (gen-list start end)
(if (> start end)
'()
(cons start
(gen-list (+ start 1) end))))
。
答案 1 :(得分:1)
这个问题的“明显”答案的问题之一是它实际上并不能很好地工作。考虑一下:
(define (gen-list start end)
(if (> start end)
'()
(cons start
(gen-list (+ start 1) end))))
好吧,如果start
比end
小得多,则堆栈上将有大量的递归调用,因为这是一个适当的递归函数:对{{1 }}是真正的通话,必须在对gen-list
的通话(这是尾部通话)发生之前返回。
处理此问题的方法是将看起来像cons
的模式变成看起来像(cons x (<recursive-call> ...))
的模式:您需要一个带有额外参数的函数,一个 accumulator 。这意味着以前是递归的调用现在是尾部调用,因此一切都很好:该过程现在是迭代的。
此问题是列表向后出现(您需要考虑这是为什么,但是经过一番思考,这很明显)。因此,您需要反转结果。幸运的是,反转列表也是一个反复的过程,所以没关系。
但是在这种情况下,您可以倒数!因此,使用本地定义的辅助功能(可以定义为顶级功能,但为什么要麻烦呢?)的简单思路如下:
(<tail-call> ... (cons x ...))
您可以看到这是倒数:对(define (gen-list low high)
(define (gla i result)
(if (< i low)
result
(gla (- i 1) (cons i result))))
(gla high '()))
的初始调用以gla
开始,然后向后构造列表。所以,现在:
high
我们想要的。
这是Scheme中的一种常见模式,因此有一个特殊的构造:名为let。因此,我们可以更习惯地将以上内容重写为:
> (gen-list 1 3)
'(1 2 3)
这与先前的答案完全相同:它只是将初始调用移到顶部,并将其与(define (gen-list low high)
(let gla ([i high] [result '()])
(if (< i low)
result
(gla (- i 1) (cons i result)))))
的本地定义组合在一起。这可能是做这种事情的惯用的Scheme方法(尽管编写Scheme比我更多的人可能会有所不同:我确实是一名CL人员,并且不可避免地具有不良品味)。
这是故事的结尾,但是我不能抗拒添加以下内容。在comp.lang.lisp过去的糟糕日子里,人们常常问一些明显的作业问题,并且由于没有业力系统,一种方法是给出一个解决问题的答案……而这是荒谬的。
因此,我们首先可以将gla
变成一个函数,该函数将传递给连续函数以进行调用,而不是知道它必须自行调用:
gla
然后,我们当然可以将(define (gen-list low high)
(let ([gla (λ (cont i result)
(if (< i low)
result
(cont cont (- i 1) (cons i result))))])
(gla gla high '())))
变成(let ([x y]) ...)
:
((λ (x) ...) y)
这是一个很好的,纯粹的答案……没有学生会想到的。
当然,另一种更为恶意的方法是显式使用Y组合器。
答案 2 :(得分:1)
只需添加尾调用递归版本
(define (gen-list start end (acc '()) #:step (step 1))
(cond ((> start end) (reverse acc))
(else (gen-list (+ start step) end (cons start acc)))))
我个人很喜欢cond
,因为你们的所有条件然后又相形之下(或else
)-这是The little Schemer
的风格,是一本很好的学习递归思维的书。
答案 3 :(得分:-2)
您不仅需要(>开始结束)基本情况,还需要(=开始结束)基本情况,在此情况下您要返回(列表开始)