方案的letrec只用于定义过程,特别是递归过程吗?我之所以问这个问题,是因为它似乎可以使用letrec绑定非过程。例如,(letrec ((x 1) (y 2)) (+ x y))。如果Scheme的letrec只用于过程,那么为什么它的语法不被限制为只允许过程呢?
考虑使用letrec来定义两个相互递归的过程:
(letrec ((is-even? (lambda (n)
(let ((n (abs n)))
(if (= n 0)
#t
(is-odd? (- n 1))))))
(is-odd? (lambda (n)
(let ((n (abs n)))
(if (= n 0)
#f
(is-even? (- n 1)))))))
(is-even? 123))如果我们使用Common的LABELS而不是Scheme的letrec,那么这两个相互递归的过程将被定义如下:
(labels ((even-p (n)
(let ((n (abs n)))
(if (= n 0)
t
(odd-p (- n 1)))))
(odd-p (n)
(let ((n (abs n)))
(if (= n 0)
nil
(even-p (- n 1))))))
(even-p 123))如果letrec只对定义过程有用,那么为什么它的语法不像LABELS那样被限制,只允许初始化表单中的过程?
发布于 2022-05-26 08:41:33
它对绑定过程非常有用,是的:letrec绑定的变量直到之后才能引用它们自己的绑定,所以类似的
(letrec ((x (list x)))
x)不起作用:请参阅我的另一个答案。
然而,首先,在类似于Lisp-1的Scheme中,坚持letrec只能用于过程是一个奇怪的限制。除了动态执行之外,它也是不可执行的:
(letrec ((x (if <something>
(λ ... (x ...))
1)))
...)更重要的是,letrec可以在这样的地方使用:
(letrec ((x (cons 1 (delay x))))
...)这很好,因为对x绑定的引用延迟了。因此,如果您有流,那么您可以这样做:
;;; For Racket
(require racket/stream)
(letrec ((ones (stream-cons 1 ones)))
(stream-ref ones 100))发布于 2022-05-26 04:30:29
它对于定义程序是有用的,但它不仅适用于此。
let*和letrec的区别被定义为在手册中。
与
let类似,包括从左到右计算val-exprs,,但是所有 ids 的位置都是首先创建的,所有id都绑定在所有val-exprs以及主体中,每个id在计算相应的val-expr之后立即初始化。
在这里,id的绑定顺序是重要的,因为let*对于定义相互递归的过程并不有用:第一个过程对于第二个过程有一个未绑定的id。
由于您的示例直接来自手册,我认为您应该看到CS信息在阅读时通常是密集而简洁的,但它确实非常清楚地说明了与letrec的区别。
https://stackoverflow.com/questions/72386609
复制相似问题