对lambda求值的函数不能用于定义范围内的新函数



在scheme中,您可以定义返回lambda表达式的函数,并使用它们来定义新函数。例如,您可以编写以下代码

(define (pow-iter base exp r)
  (if (= exp 1)
      r
      (pow-iter base (- exp 1) (* base r))))
(define (pow exp)
  (lambda (base)
    (pow-iter base exp base)))
(define cubic (pow 3))
(cubic 2)

这里有一个函数pow,它以指数为自变量,并计算为lambda函数,该函数的计算为给定基数的n次方。

然而,如果我们把它放在这样的范围内:

(define (do-cubic x)
  (define (pow-iter base exp r)
    (if (= exp 1)
        r
        (pow-iter base (- exp 1) (* base r))))
  (define (pow exp)
    (lambda (base)
      (pow-iter base exp base)))
  (define cubic (pow 3))
  (cubic x))
(do-cubic 2)

我收到一个错误

pow:未定义;在初始化之前无法使用

为什么会出现这个错误?有没有办法在不改变程序逻辑的情况下修复它?

此程序引发相同错误:

#lang r5rs
(let ()
  (define (foo x) (lambda (y) (+ 42 x)))
  (define bar (foo 1))
  (bar 2))
Output:
  foo: undefined;
  cannot use before initialization

出现错误的原因是"内部定义"被重写为letrec表达式,所有绑定在计算初始值时都有效,因此允许相互递归的定义。

(letrec ((foo (lambda (x) (lambda (y) (+ 42 x))))
         (bar (foo 1)))
 (bar 2))

在R5RS中,初始化表达式按未指定的顺序求值。这意味着在上面的第一个片段中,(define bar (foo 1))可以在(define (foo x) ...)之前被评估。换句话说,在CCD_ 6被初始化之前需要CCD_ 5的值。

在Racket(#lang racket)中,内部定义使用letrec*-语义(即初始化表达式按照它们在代码中出现的顺序进行评估。因此程序运行时没有错误。

还要注意,#lang racket中的letrec对应于letrec*在"R5RS"实现中的作用。

有关letrecletrec*的更多信息,请参阅http://www.cs.indiana.edu/~dyb/pubs/letrec-reloaded.pdf

最新更新