Scheme 变量和函数定义的顺序

Scheme 变量和函数定义的顺序,scheme,lisp,racket,definition,operator-precedence,Scheme,Lisp,Racket,Definition,Operator Precedence,为什么: 函数定义可以使用其后定义的定义 而变量定义不能 比如说, a) 以下代码片段错误: ; Must define function `f` before variable `a`. #lang racket (define a (f)) (define (f) 10) ; Function `g` could be defined after function `f`. #lang racket (define (f) (g)) ; `g` is not defined yet (d

为什么:

  • 函数定义可以使用其后定义的定义
  • 而变量定义不能
  • 比如说,

    a) 以下代码片段错误:

    ; Must define function `f` before variable `a`.
    #lang racket
    (define a (f)) 
    (define (f) 10)
    
    ; Function `g` could be defined after function `f`.
    #lang racket
    (define (f) (g)) ; `g` is not defined yet
    (define (g) 10)
    
    ; Variable `a` could be defined after function `f`
    #lang racket
    (define (f) a) ; `a` is not defined yet
    (define a 10)
    
    b) 而下面的片段是正确的:

    ; Must define function `f` before variable `a`.
    #lang racket
    (define a (f)) 
    (define (f) 10)
    
    ; Function `g` could be defined after function `f`.
    #lang racket
    (define (f) (g)) ; `g` is not defined yet
    (define (g) 10)
    
    ; Variable `a` could be defined after function `f`
    #lang racket
    (define (f) a) ; `a` is not defined yet
    (define a 10)
    
    c)

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

    以下是一个近似的一般方案说明,一个类比

    定义函数

    (define (f) (g))
    
    多少有点像

    f := (lambda () (g))
    
    因此,计算lambda表达式,并将结果函数对象(通常是闭包)存储在正在定义的新变量
    f
    中。调用函数
    f
    时,必须定义函数
    g

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

    但是

    就像

    a := (f)
    
    i、 e.函数
    f
    必须在没有参数的情况下调用,调用结果存储在定义的新变量
    a
    中。因此,此时必须已经定义函数

    文件中的每个定义都是按顺序依次执行的。每个定义都允许引用文件中定义的任何变量,包括上面和下面的变量(它们都被称为属于同一范围),但只允许使用上面定义的变量的值


    (这里有一个模棱两可的地方:假设您使用了一个内置函数,比如说带有
    (define a(+12))
    ,但后来在文件中也定义了它,比如说
    (define+-)
    。这是定义还是重新定义?在第一种情况下,是球拍的选择,在定义之前使用是禁止的。在第二种情况下,“全局”值用于计算
    a
    的值,然后重新定义函数。某些方案可能会采用这种方法。感谢Eli Barzilay向我展示了这一点,感谢Chris Jester Young的帮助)。

    以下是一个近似的通用方案描述,一个类比

    定义函数

    (define (f) (g))
    
    多少有点像

    f := (lambda () (g))
    
    因此,计算lambda表达式,并将结果函数对象(通常是闭包)存储在正在定义的新变量
    f
    中。调用函数
    f
    时,必须定义函数
    g

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

    但是

    就像

    a := (f)
    
    i、 e.函数
    f
    必须在没有参数的情况下调用,调用结果存储在定义的新变量
    a
    中。因此,此时必须已经定义函数

    文件中的每个定义都是按顺序依次执行的。每个定义都允许引用文件中定义的任何变量,包括上面和下面的变量(它们都被称为属于同一范围),但只允许使用上面定义的变量的值


    (这里有一个模棱两可的地方:假设您使用了一个内置函数,比如说带有
    (define a(+12))
    ,但后来在文件中也定义了它,比如说
    (define+-)
    。这是定义还是重新定义?在第一种情况下,是球拍的选择,在定义之前使用是禁止的。在第二种情况下,“全局”值用于计算
    a
    的值,然后重新定义函数。某些方案可能会采用这种方法。感谢Eli Barzilay向我展示此方法,感谢Chris Jester Young的帮助)。

    您需要了解有关Racket的几件事:

  • 在Racket中,每个文件(以
    #lang
    开头)都是一个模块,与许多没有模块的(传统的R5R)方案不同

  • 模块的作用域规则类似于函数的规则,因此在某种意义上,这些定义类似于函数中的定义

  • Racket从左到右评估定义。在scheme行话中,你说Racket的定义具有
    letrec*
    语义;这与一些使用
    letrec
    语义的方案不同,在这些方案中,相互递归的定义永远不起作用

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

    (define a 1)
    (define b (add1 a))
    
    它们是在一个范围内创建的——因此从理论上讲,正向定义在范围内是有效的。但实际上使用前向引用的值是行不通的,因为在计算实际值之前,您会得到一个特殊的
    值。要查看此信息,请尝试运行以下代码:

    #lang racket
    (define (foo)
      (define a a)
      a)
    (foo)
    
    模块的顶层受到进一步限制,因此此类引用实际上是错误,您可以通过以下方式看到:

    #lang racket
    (define a a)
    
    考虑到所有这些,函数中的引用更为宽松。问题是函数体在函数被调用之前是不会执行的——因此,如果函数内部发生正向引用,那么在初始化所有绑定之后调用函数是有效的(=不会得到错误或
    #
    )。这适用于普通函数定义

    (define foo (lambda () a))
    
    使用常用语法的定义

    (define (foo) a)
    
    甚至其他最终扩展为功能的形式

    (define foo (delay a))
    
    有了所有这些,您将不会根据相同的规则得到任何错误——当函数体的所有使用都发生在定义初始化之后

    然而,一个重要的注意事项是