灯下 登录
计算机科学 / SICP / subsection 中英对照

1.3.2 Constructing Procedures Using Lambda

In using sum as in 1.3.1, it seems terribly awkward to

have to define trivial procedures such as pi-term and pi-next

just so we can use them as arguments to our higher-order procedure. Rather

than define pi-next and pi-term, it would be more convenient to

have a way to directly specify “the procedure that returns its input

incremented by 4” and “the procedure that returns the reciprocal of its input

times its input plus 2.” We can do this by introducing the special form

lambda, which creates procedures. Using lambda we can describe

what we want as

在 1.3.1 节中使用 sum 时,不得不专门定义诸如 pi-term 和 pi-next 这样的琐碎过程,仅仅是为了把它们作为参数传给高阶过程 (higher-order procedure),这显得极为繁琐。与其定义 pi-next 和 pi-term,不如有一种方式能够直接描述“将输入加 4 后返回的过程”以及“返回输入与输入加 2 之积的倒数的过程”,这样更为方便。我们可以通过引入特殊形式 lambda 来实现这一点——lambda 用于创建过程。借助 lambda,我们可以将所需的内容描述为

and

以及

Then our pi-sum procedure can be expressed without defining any

auxiliary procedures as

这样,我们的 pi-sum 过程便可以写成无需定义任何辅助过程的形式:

Again using lambda, we can write the integral procedure without

having to define the auxiliary procedure add-dx:

再次利用 lambda,我们可以在不定义辅助过程 add-dx 的情况下,写出 integral 过程:

In general, lambda is used to create procedures in the same way as

define, except that no name is specified for the procedure:

一般而言,lambda 用于创建过程的方式与 define 相同,区别仅在于不为过程指定名字:

The resulting procedure is just as much a procedure as one that is created

using define. The only difference is that it has not been associated

with any name in the environment. In fact,

由此得到的过程与用 define 创建的过程没有任何区别,唯一不同之处在于它没有在环境中与任何名字相关联。事实上,

is equivalent to

与以下写法等价:

We can read a lambda expression as follows:

我们可以这样阅读一个 lambda 表达式:

Like any expression that has a procedure as its value, a lambda

expression can be used as the operator in a combination such as

与任何值为过程的表达式一样,lambda 表达式可以作为组合式中的运算符使用,例如:

or, more generally, in any context where we would normally use a procedure

name.

Subheading: Using let to create local variables

或者,更一般地,在任何通常使用过程名的上下文中都可以使用 lambda 表达式。

子标题:使用 let 创建局部变量

Another use of lambda is in creating local variables. We often need

local variables in our procedures other than those that have been bound as

formal parameters. For example, suppose we wish to compute the function

lambda 的另一个用途是创建局部变量 (local variables)。在过程中,我们常常需要除形式参数以外的局部变量。例如,假设我们希望计算函数:

f
(
x
,
y
)

=

x
(
1
+
x
y

)
2

+

y
(
1

y
)

+

(
1
+
x
y
)
(
1

y
)
,

which we could also express as

也可以写成

a

=

1
+
x
y
,

(
x
,
y
)

b

=

1

y
,

f
(
x
,
y
)

=

x

a
2

+

y
b

+

a
b
.

In writing a procedure to compute f, we would like to include as local

variables not only x and y but also the names of intermediate

quantities like a and b. One way to accomplish this is to use an

auxiliary procedure to bind the local variables:

在编写计算 f 的过程时,我们希望将中间量(如 a 和 b)的名字与 x、y 一样都作为局部变量纳入其中。实现这一目标的一种方式是使用一个辅助过程来绑定这些局部变量:

Of course, we could use a lambda expression to specify an anonymous

procedure for binding our local variables. The body of f then becomes a

single call to that procedure:

当然,我们也可以用一个 lambda 表达式来指定绑定局部变量的匿名过程。这样,f 的体就变成对该过程的一次调用:

This construct is so useful that there is a special form called let to

make its use more convenient. Using let, the f procedure could

be written as

这种构造极为有用,因此 Scheme 提供了一种名为 let 的特殊形式,使其使用更加便捷。借助 let,过程 f 可以写成:

The general form of a let expression is

let 表达式的一般形式为:

which can be thought of as saying

可以将其理解为:

The first part of the let expression is a list of name-expression pairs.

When the let is evaluated, each name is associated with the value of the

corresponding expression. The body of the let is evaluated with these

names bound as local variables. The way this happens is that the let

expression is interpreted as an alternate syntax for

let 表达式的第一部分是一个由“名字—表达式”序对构成的表。对 let 求值时,每个名字都与对应表达式的值相关联。let 的体在这些名字被绑定为局部变量的情况下求值。其实现方式是将 let 表达式解释为以下形式的等价语法:

No new mechanism is required in the interpreter in order to provide local

variables. A let expression is simply syntactic sugar for the

underlying lambda application.

解释器无需引入任何新机制便可支持局部变量——let 表达式不过是底层 lambda 应用的语法糖 (syntactic sugar)。

We can see from this equivalence that the scope of a variable specified by a

let expression is the body of the let. This implies that:

从这一等价关系可以看出,let 表达式所指定的变量的作用域 (scope) 就是该 let 的体。这意味着:

Let allows one to bind variables as locally as possible to where they

are to be used. For example, if the value of x is 5, the value of the

expression

let 允许将变量的绑定尽可能地局限于实际使用它们的位置。例如,若 x 的值为 5,则表达式

(+ (let ((x 3))
(+ x (* x 10)))
x)

is 38. Here, the x in the body of the let is 3, so the value of

the let expression is 33. On the other hand, the x that is the

second argument to the outermost + is still 5.

的值为 38。此处,let 体中的 x 为 3,因此 let 表达式的值为 33;而最外层 + 的第二个参数 x 仍然是 5。

The variables’ values are computed outside the let. This matters when

the expressions that provide the values for the local variables depend upon

variables having the same names as the local variables themselves. For

example, if the value of x is 2, the expression

变量的值是在 let 之外计算的。当为局部变量提供值的表达式中用到了与局部变量同名的变量时,这一点就显得尤为重要。例如,若 x 的值为 2,则表达式

(let ((x 3)
(y (+ x 2)))
(* x y))

will have the value 12 because, inside the body of the let, x

will be 3 and y will be 4 (which is the outer x plus 2).

的值将为 12,因为在 let 的体内,x 为 3,y 为 4(即外部的 x 加 2)。

Sometimes we can use internal definitions to get the same effect as with

let. For example, we could have defined the procedure f above as

有时我们可以用内部定义来达到与 let 相同的效果。例如,上面的过程 f 也可以定义为:

We prefer, however, to use let in situations like this and to use

internal define only for internal procedures.

不过,在这类情况下,我们倾向于使用 let,而将内部 define 保留给内部过程。

Racket #lang sicp
(lambda (x) (+ x 4))
Racket #lang sicp
(lambda (x) (/ 1.0 (* x (+ x 2))))
Racket #lang sicp
(define (pi-sum a b)
 (sum (lambda (x) (/ 1.0 (* x (+ x 2))))
 a
 (lambda (x) (+ x 4))
 b))
Racket #lang sicp
(define (integral f a b dx)
 (* (sum f (+ a (/ dx 2.0))
 (lambda (x) (+ x dx))
 b)
 dx))
Racket #lang sicp
(lambda (⟨formal-parameters⟩) ⟨body⟩)
Racket #lang sicp
(define (plus4 x) (+ x 4))
Racket #lang sicp
(define plus4 (lambda (x) (+ x 4)))
Racket #lang sicp
(lambda (x) (+ x 4))
 | | | | |
the procedure of an argument x that adds x and 4
Racket #lang sicp
((lambda (x y z) (+ x y (square z))) 1 2 3)
12
Racket #lang sicp
(define (f x y)
 (define (f-helper a b)
 (+ (* x (square a))
 (* y b)
 (* a b)))
 (f-helper (+ 1 (* x y))
 (- 1 y)))
Racket #lang sicp
(define (f x y)
 ((lambda (a b)
 (+ (* x (square a))
 (* y b)
 (* a b)))
 (+ 1 (* x y))
 (- 1 y)))
Racket #lang sicp
(define (f x y)
 (let ((a (+ 1 (* x y)))
 (b (- 1 y)))
 (+ (* x (square a))
 (* y b)
 (* a b))))
Racket #lang sicp
(let ((⟨var₁⟩ ⟨exp₁⟩)
 (⟨var₂⟩ ⟨exp₂⟩)
 …
 (⟨varₙ⟩ ⟨expₙ⟩))
 ⟨body⟩)
Racket #lang sicp
let ⟨var₁⟩ have the value ⟨exp₁⟩ and
 ⟨var₂⟩ have the value ⟨exp₂⟩ and
 …
 ⟨varₙ⟩ have the value ⟨expₙ⟩
 in ⟨body⟩
Racket #lang sicp
((lambda (⟨var₁⟩ … ⟨varₙ⟩)
 ⟨body⟩)
 ⟨exp₁⟩
 …
 ⟨expₙ⟩)
Racket #lang sicp
(define (f x y)
 (define a
 (+ 1 (* x y)))
 (define b (- 1 y))
 (+ (* x (square a))
 (* y b)
 (* a b)))