racket - 让 vs Local 在球拍中定义
问题描述
我正在处理这段代码
(letrec ([f (lambda (x)
(if (= (remainder x 5) 0)
(cons (- x) (lambda () (f (+ x 1))))
(cons x (lambda () (f (+ x 1))))))])
(lambda () (f 1))))
我想,为什么要使用 letrec 和匿名函数?我可以不使用本地和定义 - 像这样的东西吗?
(local [(define (f x) (if....]))
有什么特别的理由使用任何函数而不是本地定义吗?
解决方案
您问题的完整答案会有些复杂,因为它涉及多种语言及其历史。
但简而言之,您可以local
改用,这没有什么问题。您也可以使用letrec
. 可以说,这只是您喜欢使用哪种风格的选择。
一开始,有一个语言方案。Racket 在成为自己的语言之前是一个 Scheme 实现。然后,在 Racket 中有 HtDP 的学生语言。这三种语言相似,但在许多方面存在细微差别。
我假设您使用的是 HtDP 的学生语言,其限制是函数体必须仅由一个“事物”组成。例如,下面是无效的,因为body里面有两个“东西”。
(define (f x)
1
2)
这就是local
有用的原因:它允许您定义本地定义,而它本身只算作一个“事物”。
(define (compute-fact-x-plus-one x)
(local [(define (fact n)
(if (zero? n)
1
(* n (fact (sub1 n)))))]
(add1 (fact x))))
在真正的 Racket 语言中,没有这样的限制。你可以写:
(define (compute-fact-x-plus-one x)
(define (fact n)
(if (zero? n)
1
(* n (fact (sub1 n)))))
(add1 (fact x)))
因此,local
在真正的 Racket 语言中没有那么有用。实际上,默认情况下它不是由语言提供的。你需要专门从一个额外的库中导入它,我想大多数人甚至都不知道它的存在!
local
在 Scheme 中不存在。人们更愿意使用letrec
。所以在旧的 Racket 代码中(当它只是一个 Scheme 实现时),你会看到人们也使用letrec
了很多。在现代 Racket 中,letrec
较少使用,因为它比一系列define
s 更冗长。
但这还不是全部。如果我们看一下语言的实现,所有的define
s 系列(在现代 Racket 中)和local
(在学生语言中)最终都会在内部转换为letrec
。
总之:
local
实际上只在 HtDP 学生语言中流行define
您可以直接用现代 Racket 代码编写一系列代码。letrec
传统上使用很多,但在现代球拍中直接使用的频率较低。它仍然一直用作内部目标构造,local
以及一系列define
s 转换为。
我实际上可以在这个主题上写更多。
- Scheme有多个版本,
define
跨版本的行为是不同的。并且即使在同一个版本中,define
跨实现的行为也是不同的。 letrec
可以被看作是一系列的“优越”,define
因为你可以控制它引入的变量的范围。- 等等等等
但我认为这个答案已经足够长了。