在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:undefined;初始化之前无法使用
为什么会出现此错误,有没有办法在不改变程序逻辑的情况下修复它?
答案 0 :(得分:2)
这个程序引发同样的错误:
#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) ...)
。换句话说,在foo
初始化之前需要foo
的值。
在Racket(#lang racket
)中,内部定义使用letrec*
- 语义(即初始化表达式按照它们在代码中出现的顺序进行评估。因此程序运行时没有错误。
另请注意,letrec
中的#lang racket
对应于letrec*
在“R5RS”实施中的作用。
有关letrec
vs letrec*
的详细信息,请参阅http://www.cs.indiana.edu/~dyb/pubs/letrec-reloaded.pdf的介绍