Scheme/Racket中letrec的含义

Posted

技术标签:

【中文标题】Scheme/Racket中letrec的含义【英文标题】:Meaning of letrec in Scheme/Racket 【发布时间】:2018-11-10 16:13:37 【问题描述】:

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

【问题讨论】:

letrec 没有被翻译成任何东西。 letrec 是该语言的原始形式。 (而且,就其价值而言,在 Racket 中,let也是是 Racket 中的原始形式,而不是 lambda 之上的宏,但这更多是一种主观的设计选择。) 翻译与否是一回事。但是letrec不能有一个等价的表达式,就像letlet*有一个等价的表达式一样(比如我在我的例子中指定的)? 参见例如this 和 this 。 (免责声明:两者都是我的答案)。最后一个实际上有两个可能的扩展,用于letrecletrec*。 (所以,这甚至可能是重复的......) 【参考方案1】:

请参阅论文“修复 Letrec:忠实而有效的实现” 由 Oscar Waddell、Dipanwita Sarkar 和,以及, R. Kent Dybvig。

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

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

【讨论】:

【参考方案2】:

由于您的示例没有任何程序,也没有真正的表达式,我想说您可以将其实现为:

(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 关于修复的回答是向后兼容的,有些人已经对其进行了调整。如果您编写兼容的代码,但您不应该试图假设这一点。

【讨论】:

以上是关于Scheme/Racket中letrec的含义的主要内容,如果未能解决你的问题,请参考以下文章

Scheme / Racket中的静态变量?

删除列表的最后一个元素(方案)

letrec有啥好处?

方案:何时使用 let、let* 和 letrec? [复制]

Magnolia 在西方文化中的含义

函数中A的含义是啥,W的含义是啥,x的含义是啥,是弧度还是邻边比斜边,b的含义是啥。