据我了解,以下内容:let
,let*
,letrec
和letrec*
是Scheme / Racket中使用的合成糖。
现在,如果我有一个简单的程序:
(let ((x 1)
(y 2))
(+ x y))
它被翻译成:
((lambda (x y) (+ x y)) 1 2)
如果我有:
(let* ((x 1)
(y 2))
(+ x y))
它被翻译成:
((lambda (x) ((lambda (y) (+ x y))) 2) 1)
现在,对于我的第一个问题,我理解letrec
表达式的含义,它允许一个人在let中使用递归,但我不明白它是如何完成的。 letrec
被翻译成什么?
例如,
会是什么(letrec ((x 1)
(y 2))
(+ x y))
被翻译成?
第二个问题与letrec*
相似 - 但对于letrec*
我不明白它与letrec
有何不同?而且,letrec*
表达式将被翻译成什么?
答案 0 :(得分:3)
参见论文"修复Letrec:忠实而有效的实施 Scheme的递归绑定构造"作者:Oscar Waddell,Dipanwita Sarkar,以及 R. Kent Dybvig。
本文从一个简单的版本开始,然后继续解释更复杂的扩展:
答案 1 :(得分:1)
由于您的示例没有任何程序且没有真正的表达式,我说您可以将其实现为:
(let ((x 1) (y 2))
(+ x y))
但是为了支持表单的意图,基本的实现可能会做这样的事情:
(let ((x 'undefined) (y 'undefined))
(let ((tmpx 1) (tmpy 2))
(set! x tmpx)
(set! y tmpy))
(+ x y))
现在。 letrec
是指lambdas能够通过名称来称呼自己。想象一下:
(let ((fib (lambda (n)
(if (< n 2) n
(+ (fib (- n 1)) (fib (- n 2)))))))
(fib 10))
了解这不起作用以及为什么这一点非常重要。将其转换为lambda
调用可以很容易地看到它:
((lambda (fib)
(fib 10))
(lambda (n)
(if (< n 2) n
(+ (fib (- n 1)) (fib (- n 2))))))
不知何故,您需要在创建fib
后评估lambda。让我们想象我们这样做:
(let ((fib 'undefined))
(set! fib (lambda (n)
(if (< n 2) n
(+ (fib (- n 1)) (fib (- n 2))))))
(fib 10))
或者你可以使用Z combinator来使它变得纯净:
(let ((fib (Z (lambda (fib)
(lambda (n)
(if (< n 2) n
(+ (fib (- n 1)) (fib (- n 2)))))))))
(fib 10))
这需要实施更多的工作,但至少你没有变异。为了使它与几个相互递归绑定一起工作可能需要更多的工作,但我确信它是可行的。
这是正确执行此操作的唯一两种方法。我知道clojure
有recur
模仿递归,但实际上是一个goto。
对于不从letrec
绑定进行闭包的变量,它们作为let
使用,后来更像let*
,因为Soegaards回答有关修复的内容是向后兼容的,有些已经对其进行了调整。如果您编写兼容的代码,但不应该想要这样做。