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

2.1.1 Example: Arithmetic Operations for Rational Numbers

Suppose we want to do arithmetic with rational numbers. We want to be able to

add, subtract, multiply, and divide them and to test whether two rational

numbers are equal.

假设我们要对有理数 (rational numbers) 进行算术运算。我们希望能够对它们做加法、减法、乘法和除法,以及判断两个有理数是否相等。

Let us begin by assuming that we already have a way of constructing a rational

number from a numerator and a denominator. We also assume that, given a

rational number, we have a way of extracting (or selecting) its numerator and

its denominator. Let us further assume that the constructor and selectors are

available as procedures:

让我们从一个假设出发:我们已经有了某种方式,可以由一个分子和一个分母构造出一个有理数。同样假设,给定一个有理数,我们有办法提取(或选取)它的分子和分母。进一步假设这个构造函数和选择函数都以过程的形式提供:

(make-rat ⟨n⟩ ⟨d⟩) returns the rational number whose numerator

is the integer ⟨n⟩ and whose denominator is the integer

⟨d⟩.

(make-rat ⟨n⟩ ⟨d⟩) 返回一个有理数,其分子为整数 ⟨n⟩,分母为整数 ⟨d⟩。

(numer ⟨x⟩) returns the numerator of the rational number

⟨x⟩.

(numer ⟨x⟩) 返回有理数 ⟨x⟩ 的分子。

(denom ⟨x⟩) returns the denominator of the rational number

⟨x⟩.

(denom ⟨x⟩) 返回有理数 ⟨x⟩ 的分母。

We are using here a powerful strategy of synthesis:

wishful thinking.

We haven’t yet said how a rational number is represented, or how the procedures

numer, denom, and make-rat should be implemented. Even

so, if we did have these three procedures, we could then add, subtract,

multiply, divide, and test equality by using the following relations:

n

1

我们在这里使用了一种强有力的综合策略:一厢情愿 (wishful thinking)。我们尚未说明有理数如何表示,也没有说过程 numer、denom 和 make-rat 应当如何实现。即便如此,倘若我们确实拥有这三个过程,就可以利用下面的关系来进行加法、减法、乘法、除法和相等性判断:

n

1

d
1

+

n
2

d
2

=

n
1

d
2

+

n
2

d
1

d
1

d
2

,

n
1

d
1

n
2

d
2

=

n
1

d
2

n
2

d
1

d
1

d
2

,

n
1

d
1

×

n
2

d
2

=

n
1

n
2

d
1

d
2

,

n
1

/

d
1

n
2

/

d
2

=

n
1

d
2

d
1

n
2

,

n
1

d
1

=

n
2

d
2

i
f

a
n
d

o
n
l
y

i
f

n
1

d
2

=

n
2

d
1

.

We can express these rules as procedures:

我们可以把这些规则表述为若干过程:

Now we have the operations on rational numbers defined in terms of the selector

and constructor procedures numer, denom, and make-rat.

But we haven’t yet defined these. What we need is some way to glue together a

numerator and a denominator to form a rational number.

Subheading: Pairs

现在我们已经用选择函数和构造函数 `numer`、`denom`、`make-rat` 定义了有理数上的各种运算。但这三个过程本身尚未定义。我们需要某种方式把分子和分母"粘合"在一起,构成一个有理数。

小节标题:序对

To enable us to implement the concrete level of our data abstraction, our

language provides a compound structure called a

pair, which can be

constructed with the primitive procedure cons. This procedure takes two

arguments and returns a compound data object that contains the two arguments as

parts. Given a pair, we can extract the parts using the primitive procedures

car and cdr. Thus, we can use cons, car, and

cdr as follows:

为了让我们能够实现数据抽象的具体层次,语言提供了一种称为序对 (pair) 的复合结构,可以用基本过程 `cons` 来构造。`cons` 接受两个参数,返回一个包含这两个参数作为组成部分的复合数据对象。给定一个序对,我们可以用基本过程 `car` 和 `cdr` 分别提取其两个部分。因此,`cons`、`car` 和 `cdr` 的用法如下:

Notice that a pair is a data object that can be given a name and manipulated,

just like a primitive data object. Moreover, cons can be used to form

pairs whose elements are pairs, and so on:

请注意,序对是一种数据对象,可以被命名和操作,就像基本数据对象一样。此外,`cons` 可以用来构造元素本身也是序对的序对,依此类推:

In 2.2 we will see how this ability to combine pairs means that

pairs can be used as general-purpose building blocks to create all sorts of

complex data structures. The single compound-data primitive

pair,

implemented by the procedures cons, car, and cdr, is the

only glue we need. Data objects constructed from pairs are called

在第 2.2 节中,我们将看到这种任意组合序对的能力意味着序对可以用作通用的构造块,从而创建出各种复杂的数据结构。由 `cons`、`car` 和 `cdr` 实现的这个唯一的复合数据基本元素——序对——就是我们所需的全部"胶水"。由序对构造而成的数据对象称为

list-structured data.

Subheading: Representing rational numbers

表结构数据 (list-structured data)。

小节标题:有理数的表示

Pairs offer a natural way to complete the rational-number system. Simply

represent a rational number as a pair of two integers: a numerator and a

denominator. Then make-rat, numer, and denom are readily

implemented as follows:

序对为完成有理数系统提供了一种自然的方式。只需将有理数表示为两个整数的序对:一个分子和一个分母。这样,`make-rat`、`numer` 和 `denom` 就可以很方便地实现如下:

Also, in order to display the results of our computations, we can print

rational numbers by printing the numerator, a slash, and the

denominator:

另外,为了显示计算结果,我们可以通过依次打印分子、一条斜线和分母来打印有理数:

Now we can try our rational-number procedures:

现在我们可以试用我们的有理数过程了:

As the final example shows, our rational-number implementation does not reduce

rational numbers to lowest terms. We can remedy this by changing

make-rat. If we have a gcd procedure like the one in

1.2.5 that produces the greatest common divisor of two integers, we can

use gcd to reduce the numerator and the denominator to lowest terms

before constructing the pair:

正如最后一个例子所示,我们的有理数实现并没有将有理数化简为最简分数。我们可以通过修改 `make-rat` 来弥补这一点。若我们拥有一个类似于第 1.2.5 节中那样能求两个整数最大公约数的 `gcd` 过程,便可以在构造序对之前用 `gcd` 将分子和分母化简为最简形式:

Now we have

现在我们得到了

as desired. This modification was accomplished by changing the constructor

make-rat without changing any of the procedures (such as add-rat

and mul-rat) that implement the actual operations.

正是我们所期望的结果。这一改进是通过修改构造函数 make-rat 来实现的,而无需改动任何实现实际运算的过程(如 add-rat 和 mul-rat)。

Racket #lang sicp
(define (add-rat x y)
 (make-rat (+ (* (numer x) (denom y))
 (* (numer y) (denom x)))
 (* (denom x) (denom y))))

(define (sub-rat x y)
 (make-rat (- (* (numer x) (denom y))
 (* (numer y) (denom x)))
 (* (denom x) (denom y))))

(define (mul-rat x y)
 (make-rat (* (numer x) (numer y))
 (* (denom x) (denom y))))

(define (div-rat x y)
 (make-rat (* (numer x) (denom y))
 (* (denom x) (numer y))))

(define (equal-rat? x y)
 (= (* (numer x) (denom y))
 (* (numer y) (denom x))))
Racket #lang sicp
(define x (cons 1 2))

(car x)
1

(cdr x)
2
Racket #lang sicp
(define x (cons 1 2))
(define y (cons 3 4))
(define z (cons x y))

(car (car z))
1

(car (cdr z))
3
Racket #lang sicp
(define (make-rat n d) (cons n d))
(define (numer x) (car x))
(define (denom x) (cdr x))
Racket #lang sicp
(define (print-rat x)
 (newline)
 (display (numer x))
 (display "/")
 (display (denom x)))
Racket #lang sicp
(define one-half (make-rat 1 2))
(print-rat one-half)
1/2

(define one-third (make-rat 1 3))
(print-rat
 (add-rat one-half one-third))
5/6

(print-rat
 (mul-rat one-half one-third))
1/6

(print-rat
 (add-rat one-third one-third))
6/9
Racket #lang sicp
(define (make-rat n d)
 (let ((g (gcd n d)))
 (cons (/ n g)
 (/ d g))))
Racket #lang sicp
(print-rat
 (add-rat one-third one-third))
2/3