2.6 Top-Level定义

letlambda创建的变量绑定在表达式外部是不可见的。假设想创建一个对象,也许是一个函数,想要在任意地方访问,比如+cons。你需要的是通过define形式进来一个顶级定义 (top-level definition)。顶级定义被大多数交互式 Scheme 系统所支持,在你输入的任何表达式中是可见的,除非它被另一个绑定所遮蔽。

我们把上面一节中的double-any实现为一个顶级定义。

(define double-any
  (lambda (f x)
    (f x x)))

现在,double-any拥有了和cons这样的内置函数一样的地位。我们可以使用像调用一个内置函数一样来调用它

(double-any + 10) => 20
(double-any cons 'a) => (a . a)

一个顶级定义可以使用任意对象来建立,并不仅仅是函数。

顶级定义可以被letlambda绑定所遮蔽。

(define xyz '(x y z))
(let ((xyz '(z y x)))
  xyz) => (z y x)

Variables with top-level definitions act almost as if they were bound by a let expression enclosing all of the expressions you type.

到此为止,你仅仅了解到一些简单的工具,然而,已经可以通过它们来定义一些Scheme的内置函数,后面将要讲到。如果你完成了上一节的练习,你已经知道如何定义list函数了

(define list (lambda x x))

同样,Scheme 提供了缩写的 cadrcddr(cadr list) 相当于 (car (cdr list)),(cddr list)相当于(cdr (cdr list)),它们可以很容易地定义出来

(define cadr
  (lambda (x)
    (car (cdr x))))

(define cddr
  (lambda (x)
    (cdr (cdr x))))

(cadr '(a b c)) => b
(cddr '(a b c)) => (c)

当定义一个函数的时候(define var expr),当expr是一个lambda表达式,可以使用一种简短的写法来省略掉 lambda

(define var0
  (lambda (var1 ... varn)
    e1 e2 ...))

可以缩写为

(define (var0 var1 ... varn)
  e1 e2 ...)


(define var0
  (lambda varr
    e1 e2 ...))

可以缩写为

(define (var0 . varr)
  e1 e2 ...)

还有

(define var0
  (lambda (var1 ... varn . varr)
    e1 e2 ...))

可以缩写为

(define (var0 var1 ... varn . varr)
  e1 e2 ...)

上面的 cadr 函数及list函数可以写为:

(define (cadr x)
  (car (cdr x)))

(define (list . x) x)

本书并不经常使用这种缩写,虽然它更短一点,但是它往往倾向于掩盖掉函数并不总是与其它语言当中的变量、名字联系在一起这个事实。(完全不知道怎么翻译,大概意思也许是:Scheme的函数与其它语言的函数并不等价,在Scheme中,可以把一个普通的值赋给一个变量,也可以把一个函数赋值给一个变量,函数与普通的对象没有本质的区别).This syntax is often referred to, somewhat pejoratively, as the "defun" syntax for define, after the defun form provided by Lisp languages in which procedures are more closely tied to their names.

看来,Dybvig 更推崇 Lisp 的 defun 语法。

顶级定义使用得交互地测试一个函数变得简单,因为我们不需要每次都重新输入函数的完整定义。我们来定义一个复杂一点的double-any,将一个普通的两个参数的函数转变为一个倍增的单参数函数。

(define doubler
  (lambda (f)
    (lambda (x) (f x x))))

doubler接受一个参数,该参数必须是一个函数。然后 doubler 返回另一个函数,该函数接受两个参数,然后应用到 f 上面。通过doubler函数,doubledouble-cons函数可以简单地制造出来

(define double (doubler +))
(double 13/2) => 13

(define double-cons (doubler cons))
(double-cons 'a) => (a . a)

我们也可以通过 doubler 定义出 double-any:

(define double-any
  (lambda (f x)
    ((doubler f) x)))

当你尝试使用一个未通过define或者let && lambda 绑定的变量时会发生什么?

(i-am-not-defined 3)

大多数的 Scheme 实现会抛出一个异常,提示变量未绑定。

(define proc1
  (lambda (x y)
    (proc2 y x)))

上面的定义引用了一个未定义的函数 proc2,在定义中引用其它未定义的函数不是一个错误,除非你调用了它。

之后再补上proc2的定义就OK了。

(define proc2 cons)
(proc1 'a 'b) => (b . a)

在定义 proc1 的时候,系统接受你稍后会对 proc2 进行定义的承诺,不会引发任何异常,除非你在定义 proc2 之前就调用了 proc1.

这个特性允许你以任意你喜欢的顺序来定义函数,这使得可以以一种更容易阅读的方式在一个文件中组织代码。

results matching ""

    No results matching ""