1.3.2 使用LAMDBA 组合程序
在1.3.1部分中使用的SUM函数,似乎太麻烦了,为了在高阶函数中
作为参数使用,而不得不定义一些如PI-TERM之类的临时性的函数。
有更方便的直接使用的匿名方式来引用这些方法,而不是定义TERM。
我们能通过引入一个特定的符号,LAMDA来实现这一点。
LAMDA定义程序,使用LAMDA我们能够描述我们想要的如下:
(lambda (x) (+ x 4))
和
(lambda (x) (/ 1.0 (* x (+ x 2))))
然后 我们的程序pi-sum 能够在不用定义附加的程序的情况下表达成如下的样子:
(define (pi-sum a b)
(sum (lambda (x) (/ 1.0 (* x (+ x 2))))
a
(lambda (x) (+ x 4))
b
)
)
再一次地使用lambda,我们能够写出程序integral 而不用定义附加的程序add-dx:
(define (integral f a b dx)
(* (sum f
(+ a (/ dx 2.0))
(lambda (x) (+ x dx))
b)
dx
)
)
总之,lambda被用来创建程序与define一样的方式,除了没有为程序指定程序名称:
(lambda (<formal-parameters>) <body>)
lambda得到的程序与 define的程序一样的,仅有的区别是它没有关联到环境变量中的
任何一个名称。在事实上,
(define (plus4 x) (+ x 4))
等价于
(define plus4 (lambda (x) (+ x 4) ))
我们能够如下的读一个lambda表达式:
(lambda (x) (+ x 4) )
程序 有一个参数X 函数体是 X+4
像任何一个表达式一样,表达式作为一个程序有它自己的值。
一个lambda表达式能在一个表达式中作为一个操作符使用。例如:
((lambda (x y z) (+ x y (square z))) 1 2 3)
12
或者更通俗地说,我们可以任何地方像使用程序名一样地使用它。
使用LET创建局部变量
lambda的另一个用途是创建局部变量。在我们的程序中,我们常常需要局部变量,而不是
被绑定的形式参数。例如,我们要计算如下的函数公式。
f(x,y)=x(1+xy)^2+y(1-y)+(1+xy)(1-y)
我们也能表达为如下的式子:
a=1+xy
b=1-y
f(x,y)=xa^2+yb+ab
在写程序计算f时,我们想要包括的局部变量不仅有x,y 也有中间变量的名称a和 b
完成这个任务的一种方式是使用辅助的程序来绑定局部变量.
(define (f x y)
(define (f-helper a b)
(+ (* x (square a))
(* y b)
(* a b)))
(f-helper (+ 1 (* x y)) (- 1 y))
)
当然了,为了绑定我们的局部变量,我们能够使用一个lambda表达式来指定一个匿名的程序
程序F的程序体就成了对匿名程序的一个简单的调用.
(define (f x y)
((lambda (a b)
(+ (* x (square a)) (* y b) (* a b))
)
(+ 1 (* x y)) (- 1 y)
)
)
这种结构太有用了,为此特别加了一个语法结构叫做LET,以使它使用起来更方便.
使用LET,程序F,写出来如下:
(define (f x y)
(let ( (a (+ 1 (* x y)))
(b (-1 y))
)
(+ (* x (square a))
(* y b)
(* a b)
)
)
)
let表达式的通用格式如下:
(let ( (<var1> <exp1>)
(<var2> <exp2>)
.....
(<varn> <expn>)
)
<body>
)
读作 在<BODY>中 让
变量1等于表达式1的值,
变量2等于表达式2的值,等等,
一直到N
变量N等于表达式N的值.
let表达式的第一部分是名称和表达式的数对的列表.
当let被执行时,任何一个变量名称都被赋于了相应的表达式的值.
let的程序体被执行时,这些名称被绑定为局部变量. let表达式的这种执行方式,与如下的语法是一致的.
((lambda (<var1> ...<varn>)
<body>)
<exp1> ....<expn>)
为了提供局部变量,在解释器中没有提供新的机制.LET表达式只是为了
使用LAMDBA表达式的一种简单的语法糖罢了。
从这种等价中,我们能够看到LET表达式中指定的变量的作用域是LET的程序体。看如下的实例:
让我们看一个变量绑定为局部变量的例子。例如,如果X等于5,下面的表达式的值是38。
(+ (let ((x 3))
(+ x (* x 10))
) x)
在此,LET程序体中的X是3,LET表达式的值是33,外层的+程序的第二个参数X的值还是5。
变量的值被计算在LET的外面.当表达式需要为局部变量提供值时,它依赖于与局部变量同名的外部变量的值.例如,X的值是2,如下的表达式的值是12.
(let ((x 3) (y (+ x 2)))
(* x y))
因为LET表达式的内部,X等于3,Y等于4,也就是外部的X加上2。
有时我们能够使用内部定义来达到和LET表达式一样的效果。例如我们能够定义上述的程序以如下的写法。
(define (f x y)
(define a (+ 1 (* x y)))
(define b (- 1 y))
(+ (* x (square a)) (* y b) (* a b))
)
在这种情况下,我们优先使用LET。
练习1。34 假定我们定义程序
(define (f g) (g 2))
然后我们有如下的
(f square)
4
(f (lambda (z) (* z (+ z 1))))
6
当我们让解释器执行(f f)时,会发生什么情况呢?解释一下。