Racket中lambda函数的求值顺序

3

目前正在学习 Racket Guide,参考网站链接为https://docs.racket-lang.org,同时在了解 lambda 函数。虽然对其实用性的解释很清晰,但我还不确定这些函数的执行顺序。以下是指南中的一个例子:

(define (twice f v)
  (f (f v)))

(define (make-add-suffix s2)
  (lambda (s) (string-append s s2)))

(twice (make-add-suffix "!") "hello")

这里对 twice 的函数调用被认为是评估为 "hello!!"。下面是我猜测的评估过程:

(twice (make-add-suffix "!") "hello")

((make-add-suffix "!") ((make-add-suffix "!") "hello")

((make-add-suffix "!") (string-append "hello" "!"))

(string-append (string-append "hello" "!") "!")

(string-append "hello!" "!")

"hello!!"

这是一个准确的评估吗,还是我漏掉了什么重要的部分?


Make-add-suffix 只被调用一次。生成的值是一个函数,会被调用两次。大多数编程语言(但也有例外)不会像你的扩展那样进行文本重写和多次调用。 - Joshua Taylor
2个回答

2
口号: 最外层和最左侧的嵌套表达式应该首先进行评估。
(twice (make-add-suffix "!") "hello")

; (define (f x) ...) is short for (define f (lambda (x) ...)) so,
; = { substitute twice with  (lambda (f v) (f (f v)))}

((lambda (f v) (f (f v))) (make-add-suffix "!") "hello")

; = { substition of make-add-suffix with (lambda (s2) (lambda (s) (string-append s s2)))}

((lambda (f v) (f (f v)))
 ((lambda (s2) (lambda (s) (string-append s s2))) "!")
 "hello")

在我们继续之前需要了解的术语:

  • Beta Reduction: ((lambda (x-1 ... x-n) f-body) v-1 ... v-n) == f-body 将所有出现的 x-1 ... x-n 分别替换为 v-1 ... v-n

  • Call by Value: 在 Beta Reduction 之前对函数调用的参数进行求值。

; = { beta-reduction of ((lambda (s2) (lambda (s) (string-append s s2))) "!") }

((lambda (f v) (f (f v))) (lambda (s) (string-append s "!")) "hello")

; = { beta-reduction of the whole expression }

((lambda (s) (string-append s "!"))
 ((lambda (s) (string-append s "!")) "hello"))

; = { beta-reduction of the expression in the argument position first }

((lambda (s) (string-append s "!")) (string-append "hello" "!"))

; ... and the rest is easy:
((lambda (s) (string-append s "!")) "hello!")
(string-append "hello!" "!")
"hello!!"

1
通过不同的方式得到相同的答案:DrRacket 包含一个名为 "Stepper" 的工具,用于这个目的。如果将语言级别设置为“Intermediate Student with Lambda”,并单击“Step”按钮,则应该能够看到程序的求值过程作为一系列步骤进行,就像 thrvshl 描述的那样。
编辑:您描述的求值策略,其中将 twice 的第一个参数替换为 x 的定义中的每个实例,称为“call-by-name”求值,并与 Haskell 中的 laziness 相关联。为了看到区别,请考虑包含内部 lambda 中的 printfmake-add-suffix 的版本。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接