让 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 在其基本语言中同时具有 defineletrec,它们是同一回事。

let朋友们没有程序的糖就是倒霉。到 Scheme 制作时,他们可能还没有简短的形式,当他们制作它时,没有办法修补 let 和朋友。如果您不喜欢 lambda,请使用 define(或 local