变量和函数定义的顺序

时间:2013-10-29 10:01:37

标签: scheme lisp racket definition operator-precedence

为什么会这样:

  1. 函数定义可以使用
  2. 之后定义的定义
  3. 而变量定义则不能。
  4. 例如,

    a)以下代码段错误:

    ; Must define function `f` before variable `a`.
    #lang racket
    (define a (f)) 
    (define (f) 10)
    

    b)以下代码段是正确的:

    ; Function `g` could be defined after function `f`.
    #lang racket
    (define (f) (g)) ; `g` is not defined yet
    (define (g) 10)
    

    c)正确

    ; Variable `a` could be defined after function `f`
    #lang racket
    (define (f) a) ; `a` is not defined yet
    (define a 10)
    

2 个答案:

答案 0 :(得分:3)

您需要了解有关Racket的一些事项:

  1. 在Racket中,每个文件(以#lang开头)都是一个模块,不像许多(传统的,r5rs)方案没有模块。

  2. 模块的范围规则类似于函数的规则,因此从某种意义上说,这些定义类似于函数中的定义。

  3. 球拍从左到右评估定义。在方案术语中,你说Racket的定义有letrec*语义;这与使用letrec语义的一些方案不同,其中相互递归的定义永远不会起作用。

  4. 所以底线是定义都是在模块的环境中创建的(类似于函数,对于函数本地定义),然后从左到右进行初始化。因此,后向引用始终有效,因此您可以始终执行类似

    的操作
    (define a 1)
    (define b (add1 a))
    

    它们是在一个范围内创建的 - 因此理论上前向定义在它们在范围内的意义上是有效的。但实际上使用正向引用的值是行不通的,因为在得到实际值之前,您会得到一个特殊的#<undefined>值。要查看此内容,请尝试运行此代码:

    #lang racket
    (define (foo)
      (define a a)
      a)
    (foo)
    

    模块的顶层进一步受到限制,因此这些引用实际上是错误,您可以看到:

    #lang racket
    (define a a)
    

    考虑到所有这些,事情对函数内部的引用更加宽容。问题是函数的主体在调用函数之前不会执行 - 所以如果函数内部发生了正向引用,那么它是有效的(=不会得到错误或#<undefined>)在初始化所有绑定后调用。这适用于普通函数定义

    (define foo (lambda () a))
    

    使用通常的语法糖的定义

    (define (foo) a)
    

    甚至是最终扩展到函数

    的其他形式
    (define foo (delay a))
    

    通过所有这些,当初始化定义之后,当函数体的所有使用都发生时,您将不会通过相同的规则获得任何错误。

    然而,一个重要的注意事项是,您不应该将此类初始化与赋值混淆。这意味着像

    这样的事情
    (define x (+ x 1))
    

    等同于主流语言中的x = x+1。它们更像是一种语言中的某些var x = x+1,它会因某些“未初始化变量的引用”错误而失败。这是因为define在当前范围内创建绑定,它不会“修改”现有绑定。

答案 1 :(得分:2)

以下是近似的一般方案描述,类比。

定义一个功能

(define (f) (g))

或多或少像

f := (lambda () (g))

因此评估lambda表达式,并将得到的函数对象(通常是闭包)存储在正在定义的新变量f中。当调用函数g时,必须定义函数f

同样,(define (h) a)h := (lambda () a)类似,因此只有在调用函数h时,才会检查对变量a的引用,以找到其值。

但是

(define a (f))

就像

a := (f)

即。必须在没有参数的情况下调用函数f,并且定义存储在新变量a中的调用的结果。因此,必须在此时定义函数。

文件中的每个定义都是一个接一个地按顺序执行。允许每个定义引用文件中定义的任何变量,无论是在它上面还是下面(它们都被认为属于相同的范围,但是允许仅使用其上面定义的变量的

(这里有一个含糊不清的地方:假设你使用的是内置的函数,比如说(define a (+ 1 2)),但是后来也在文件中定义它,比如说(define + -)。是定义还是重新定义?在第一种情况下,是Racket的选择,禁止在定义之前使用。在第二种情况下,&#34 ; global&#34; value用于计算a的值,然后重新定义函数。一些方案可能会走这条路。感谢Eli Barzilay向我展示这一点,以及Chris Jester-Young帮忙)。