我正在阅读The Little Schemer这本书,开始学习用 Lisp 进行思考。当你进入它并真正涵盖了 lambdas 的使用时,'remove' 过程以以下一般形式编写,它返回一个用于任意 test 的删除过程test?
:
(define rember-f
(lambda (test?)
(lambda (a l)
(cond
((null? l) (quote ()))
((test? (car l) a) (cdr l))
(else (cons (car l)
((rember-f test?) a (cdr l))))))))
我理解它是如何工作的,但简单的阅读表明,在每个递归步骤中,rember-f
再次调用该过程以生成一个新的封闭过程。这意味着当您在列表上调用返回的过程时,它会再次调用以重新rember-f
生成相同的过程,然后该新的过程称为递归(如果不清楚,请参阅下面的修复)。我知道这可能会被优化掉,但是代替不知道它是否是(并且无论如何也试图让我了解这种语法),我在一些实验后设法将递归移动到过程本身而不是封闭程序如下:
(define rember-f
(lambda (test?)
(define retfun
(lambda (a l)
(cond
((null? l) (quote ()))
((test? (car l) a) (cdr l))
(else (cons (car l) (retfun a (cdr l)))))))
retfun))
我已经验证这可以按预期工作。返回值是一个删除与值 (arg 1) 匹配的列表 (arg 2) 的第一个元素的过程。在我看来,这个只调用rember-f
一次,这保证它只生成一个封闭的过程(这次有一个名字,retfun
)。
这对我来说实际上很有趣,因为与通常的尾调用优化不同,它不消耗调用堆栈上的空间,因此使递归与迭代一样有效,在这种情况下,编译器必须确定(rember-f test?)
封闭过程范围未修改因此将其替换为相同的返回值,即匿名(lambda (a l) ...)
。得知解释器/编译器没有捕捉到这一点,我一点也不感到惊讶。
是的,我知道方案是一种规范,并且有许多实现,它们在不同程度上获得了各种函数式编程优化。我目前正在通过在 guile REPL 中进行实验来学习,但会对不同的实现在这个问题上的比较感兴趣。
有谁知道 Scheme在这种情况下应该如何表现?