让 vs Local 在球拍中定义
Let vs Local define in racket
我正在处理这段代码
(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
。可以说,这只是一种风格选择,您更喜欢使用哪一种。
一开始,有一个语言Scheme。在成为自己的语言之前,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
更冗长。
但这还不是全部。如果我们看一下语言的实现,所有 define
系列(在现代 Racket 中)和 local
(在学生语言中)最终都会在内部转换为 letrec
。
总结:
local
实际上只在 HtDP 学生语言中流行
- 你可以直接用现代Racket代码写一系列
define
。
letrec
传统上用的比较多,但是在现代Racket中直接用的比较少。它仍然一直用作 local
和一系列 define
转换为的内部目标构造。
我实际上可以写更多关于这个主题的文章。
- Scheme有多个版本,
define
跨版本行为不同。并且即使在同一个版本中,define
跨实现的行为也是不同的。
letrec
可以看作是一系列 define
的“高级”,因为您可以控制它引入的变量范围。
- 等等等等
不过我觉得这个回答已经够长了。
每次你看到 (define (something ...) ..)
它都会被语言重写 (define something (lambda (...) ...))
。只是语法糖,方便写程序。
local
是不是被Scheme继承的。事实上,更具体的语法是将 define
放在过程主体中:
;;; standard Scheme letrec
(define (reverse lst)
(letrec ([helper (lamda (lst acc)
(if (null lst)
acc
(helper (cdr lst) (cons (car lst) acc))))])
(helper lst '()))
;;; Standard scheme local define
(define (reverse lst)
(define (helper (lst acc)
(if (null lst)
acc
(helper (cdr lst) (cons (car lst) acc))))
(helper lst '()))
;;; Implementation specific Racket way
(define (reverse lst)
(local [(define (helper (lst acc)
(if (null lst)
acc
(helper (cdr lst) (cons (car lst) acc))))]
(helper lst '()))
所有这些都是平等的。事实上,标准 Scheme 报告解释了局部 define
可以通过将其重写为 letrec
来实现,反之亦然。
local
版本是Racket语言独有的,不是Scheme的一部分。例如。如果您正在编写应该跨实现工作的代码,则不能使用它。至于你的问题,因为标准 Scheme 在其基本语言中同时具有 define
和 letrec
,它们是同一回事。
那let
朋友们没有程序的糖就是倒霉。到 Scheme 制作时,他们可能还没有简短的形式,当他们制作它时,没有办法修补 let
和朋友。如果您不喜欢 lambda
,请使用 define
(或 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
。可以说,这只是一种风格选择,您更喜欢使用哪一种。
一开始,有一个语言Scheme。在成为自己的语言之前,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
更冗长。
但这还不是全部。如果我们看一下语言的实现,所有 define
系列(在现代 Racket 中)和 local
(在学生语言中)最终都会在内部转换为 letrec
。
总结:
local
实际上只在 HtDP 学生语言中流行- 你可以直接用现代Racket代码写一系列
define
。 letrec
传统上用的比较多,但是在现代Racket中直接用的比较少。它仍然一直用作local
和一系列define
转换为的内部目标构造。
我实际上可以写更多关于这个主题的文章。
- Scheme有多个版本,
define
跨版本行为不同。并且即使在同一个版本中,define
跨实现的行为也是不同的。 letrec
可以看作是一系列define
的“高级”,因为您可以控制它引入的变量范围。- 等等等等
不过我觉得这个回答已经够长了。
每次你看到 (define (something ...) ..)
它都会被语言重写 (define something (lambda (...) ...))
。只是语法糖,方便写程序。
local
是不是被Scheme继承的。事实上,更具体的语法是将 define
放在过程主体中:
;;; standard Scheme letrec
(define (reverse lst)
(letrec ([helper (lamda (lst acc)
(if (null lst)
acc
(helper (cdr lst) (cons (car lst) acc))))])
(helper lst '()))
;;; Standard scheme local define
(define (reverse lst)
(define (helper (lst acc)
(if (null lst)
acc
(helper (cdr lst) (cons (car lst) acc))))
(helper lst '()))
;;; Implementation specific Racket way
(define (reverse lst)
(local [(define (helper (lst acc)
(if (null lst)
acc
(helper (cdr lst) (cons (car lst) acc))))]
(helper lst '()))
所有这些都是平等的。事实上,标准 Scheme 报告解释了局部 define
可以通过将其重写为 letrec
来实现,反之亦然。
local
版本是Racket语言独有的,不是Scheme的一部分。例如。如果您正在编写应该跨实现工作的代码,则不能使用它。至于你的问题,因为标准 Scheme 在其基本语言中同时具有 define
和 letrec
,它们是同一回事。
那let
朋友们没有程序的糖就是倒霉。到 Scheme 制作时,他们可能还没有简短的形式,当他们制作它时,没有办法修补 let
和朋友。如果您不喜欢 lambda
,请使用 define
(或 local
)