为什么我们已经有 let 时还需要 letrec?
Why is letrec necessary when we already have let?
在R7RS-small section 4.2.2 Binding constructs中,有一个例子letrec
:
(letrec ((even?
(lambda (n)
(if (zero? n)
#t
(odd? (- n 1)))))
(odd?
(lambda (n)
(if (zero? n)
#f
(even? (- n 1))))))
(even? 88))
我不明白为什么 letrec
是必要的,因为我们的语言已经有了 let
。我尝试用 let
替换 letrec
,并且在 MIT Scheme 11.2、Racket 7.2 plt-r5rs
、Chez Scheme 9.5 和 Guile 3.0 中得到了正确的值 (#t
)。 1. letrec
的真正用途是什么?
(let ((even?
(lambda (n)
(if (zero? n)
#t
(odd? (- n 1)))))
(odd?
(lambda (n)
(if (zero? n)
#f
(even? (- n 1))))))
(even? 88))
letrec
不能这样换成let
。它 'works' 在你的情况下,因为你的本地绑定正在隐藏全局绑定,然后在看起来递归但不是递归的调用中调用它们。考虑一下
(let ((factorial
(λ (n)
(if (<= n 1)
1
(* n (factorial (- n 1)))))))
(factorial 10))
如果您尝试对此进行评估,您将收到错误消息。但是,如果您使用 letrec
,则不会:
(letrec ((factorial
(λ (n)
(if (<= n 1)
1
(* n (factorial (- n 1)))))))
(factorial 10))
很好。
当然,如果您愿意通过将函数本身作为参数传递来使用与 U 组合器非常相似的东西,那么您实际上可以不用 letrec
就可以离开:
(let ((factorial
(λ (c n)
(if (<= n 1)
1
(* n (c c (- n 1)))))))
(factorial factorial 10))
但是,好吧,你也不需要let
:几乎所有的东西都只是一种语法上的便利。
在R7RS-small section 4.2.2 Binding constructs中,有一个例子letrec
:
(letrec ((even?
(lambda (n)
(if (zero? n)
#t
(odd? (- n 1)))))
(odd?
(lambda (n)
(if (zero? n)
#f
(even? (- n 1))))))
(even? 88))
我不明白为什么 letrec
是必要的,因为我们的语言已经有了 let
。我尝试用 let
替换 letrec
,并且在 MIT Scheme 11.2、Racket 7.2 plt-r5rs
、Chez Scheme 9.5 和 Guile 3.0 中得到了正确的值 (#t
)。 1. letrec
的真正用途是什么?
(let ((even?
(lambda (n)
(if (zero? n)
#t
(odd? (- n 1)))))
(odd?
(lambda (n)
(if (zero? n)
#f
(even? (- n 1))))))
(even? 88))
letrec
不能这样换成let
。它 'works' 在你的情况下,因为你的本地绑定正在隐藏全局绑定,然后在看起来递归但不是递归的调用中调用它们。考虑一下
(let ((factorial
(λ (n)
(if (<= n 1)
1
(* n (factorial (- n 1)))))))
(factorial 10))
如果您尝试对此进行评估,您将收到错误消息。但是,如果您使用 letrec
,则不会:
(letrec ((factorial
(λ (n)
(if (<= n 1)
1
(* n (factorial (- n 1)))))))
(factorial 10))
很好。
当然,如果您愿意通过将函数本身作为参数传递来使用与 U 组合器非常相似的东西,那么您实际上可以不用 letrec
就可以离开:
(let ((factorial
(λ (c n)
(if (<= n 1)
1
(* n (c c (- n 1)))))))
(factorial factorial 10))
但是,好吧,你也不需要let
:几乎所有的东西都只是一种语法上的便利。