Ben*_*Ben 1 lisp scheme definition operator-precedence racket
为什么是这样:
例如,
a) 以下代码片段是错误的:
; Must define function `f` before variable `a`.
#lang racket
(define a (f))
(define (f) 10)
Run Code Online (Sandbox Code Playgroud)
b) 虽然以下片段是正确的:
; Function `g` could be defined after function `f`.
#lang racket
(define (f) (g)) ; `g` is not defined yet
(define (g) 10)
Run Code Online (Sandbox Code Playgroud)
C)右太:
; Variable `a` could be defined after function `f`
#lang racket
(define (f) a) ; `a` is not defined yet
(define a 10)
Run Code Online (Sandbox Code Playgroud)
您需要了解 Racket 的几件事:
在 Racket 中,每个文件(以 开头#lang)都是一个模块,这与许多没有模块的(传统的,r5rs)方案不同。
模块的作用域规则类似于函数的规则,所以从某种意义上说,这些定义类似于函数中的定义。
Racket 从左到右评估定义。在计划行话中,您说 Racket 的定义具有letrec*语义;这与一些使用letrec语义的方案不同,其中相互递归定义永远不起作用。
所以底线是定义都是在模块的环境中创建的(类似在函数中,对于函数局部定义),然后从左到右初始化它们。因此,反向引用始终有效,因此您始终可以执行以下操作
(define a 1)
(define b (add1 a))
Run Code Online (Sandbox Code Playgroud)
它们是在单一范围内创建的——因此理论上,前向定义在它们在范围内的意义上是有效的。但实际上使用前向引用的值是行不通的,因为#<undefined>在评估实际值之前您会得到一个特殊值。要看到这一点,请尝试运行以下代码:
#lang racket
(define (foo)
(define a a)
a)
(foo)
Run Code Online (Sandbox Code Playgroud)
模块的顶层受到进一步限制,因此此类引用实际上是错误,您可以通过以下方式查看:
#lang racket
(define a a)
Run Code Online (Sandbox Code Playgroud)
考虑到所有这些,对于函数内部的引用,事情会稍微宽松一些。问题是函数的主体在函数被调用之前不会被执行——所以如果一个前向引用发生在一个函数内部,它是有效的(= 不会得到错误或#<undefined>)如果函数在所有之后被调用绑定已初始化。这适用于普通函数定义
(define foo (lambda () a))
Run Code Online (Sandbox Code Playgroud)
使用常用语法糖的定义
(define (foo) a)
Run Code Online (Sandbox Code Playgroud)
甚至其他最终扩展为功能的形式
(define foo (delay a))
Run Code Online (Sandbox Code Playgroud)
有了所有这些,您将不会因为相同的规则而得到任何错误——当函数体的所有使用发生在定义被初始化之后。
但是,一个重要的注意事项是您不应将这种初始化与赋值混淆。这意味着像
(define x (+ x 1))
Run Code Online (Sandbox Code Playgroud)
是不是等同于x = x+1主流语言。它们更像var x = x+1是一种语言中的一些,会因一些“对未初始化变量的引用”错误而失败。这是因为在当前范围内define创建了一个新的绑定,它不会“修改”现有的绑定。