首页 > 解决方案 > 让 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....]))

有什么特别的理由使用任何函数而不是本地定义吗?

标签: racket

解决方案


您问题的完整答案会有些复杂,因为它涉及多种语言及其历史。

但简而言之,您可以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较少使用,因为它比一系列defines 更冗长。

但这还不是全部。如果我们看一下语言的实现,所有的defines 系列(在现代 Racket 中)和local(在学生语言中)最终都会在内部转换为letrec

总之:

  • local实际上只在 HtDP 学生语言中流行
  • define您可以直接用现代 Racket 代码编写一系列代码。
  • letrec传统上使用很多,但在现代球拍中直接使用的频率较低。它仍然一直用作内部目标构造,local以及一系列defines 转换为。

我实际上可以在这个主题上写更多。

  • Scheme有多个版本,define跨版本的行为是不同的。并且即使在同一个版本中,define跨实现的行为也是不同的。
  • letrec可以被看作是一系列的“优越”,define因为你可以控制它引入的变量的范围。
  • 等等等等

但我认为这个答案已经足够长了。


推荐阅读