Scheme / Racket中{let}的含义

时间:2018-05-31 15:26:51

标签: scheme racket let letrec

据我了解,以下内容:letlet*letrecletrec*是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*表达式将被翻译成什么?

2 个答案:

答案 0 :(得分:3)

参见论文"修复Letrec:忠实而有效的实施 Scheme的递归绑定构造"作者:Oscar Waddell,Dipanwita Sarkar,以及 R. Kent Dybvig。

本文从一个简单的版本开始,然后继续解释更复杂的扩展:

https://www.cs.indiana.edu/~dyb/pubs/fixing-letrec.pdf

答案 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))

这需要实施更多的工作,但至少你没有变异。为了使它与几个相互递归绑定一起工作可能需要更多的工作,但我确信它是可行的。

这是正确执行此操作的唯一两种方法。我知道clojurerecur模仿递归,但实际上是一个goto。

对于不从letrec绑定进行闭包的变量,它们作为let使用,后来更像let*,因为Soegaards回答有关修复的内容是向后兼容的,有些已经对其进行了调整。如果您编写兼容的代码,但不应该想要这样做。