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

2.1.2 Abstraction Barriers

Before continuing with more examples of compound data and data abstraction, let

us consider some of the issues raised by the rational-number example. We

defined the rational-number operations in terms of a constructor

make-rat and selectors numer and denom. In general, the

underlying idea of data abstraction is to identify for each type of data object

a basic set of operations in terms of which all manipulations of data objects

of that type will be expressed, and then to use only those operations in

manipulating the data.

在继续讨论更多复合数据与数据抽象的例子之前,让我们先审视一下有理数例子所引发的若干问题。我们用构造函数 make-rat 和选择函数 numer、denom 定义了有理数运算。数据抽象的基本思想在于:对于每一种数据对象类型,确定一组基本操作,所有对该类型数据对象的处理都将仅通过这组操作来表达,并且在操作数据时也只使用这些操作。

We can envision the structure of the rational-number system as shown in

Figure 2.1. The horizontal lines represent

abstraction barriers

that isolate different “levels” of the system. At each level, the

barrier separates the programs (above) that use the data abstraction from the

programs (below) that implement the data abstraction. Programs that use

rational numbers manipulate them solely in terms of the procedures supplied

“for public use” by the rational-number package: add-rat,

sub-rat, mul-rat, div-rat, and equal-rat?. These,

in turn, are implemented solely in terms of the constructor and selectors

make-rat, numer, and denom, which themselves are

implemented in terms of pairs. The details of how pairs are implemented are

irrelevant to the rest of the rational-number package so long as pairs can be

manipulated by the use of cons, car, and cdr. In effect,

procedures at each level are the interfaces that define the abstraction

barriers and connect the different levels.

Figure 2.1: Data-abstraction barriers in the rational-number package.

我们可以将有理数系统的结构设想为图 2.1 所示的样子。图中的水平线代表抽象屏障 (abstraction barriers),它们将系统的不同"层次"隔离开来。在每个层次上,屏障将使用数据抽象的程序(上方)与实现数据抽象的程序(下方)分隔开。使用有理数的程序仅通过有理数包"对外公开"的过程来操作有理数:add-rat、sub-rat、mul-rat、div-rat 和 equal-rat?。而这些过程本身又仅通过构造函数和选择函数 make-rat、numer 和 denom 来实现,后者再通过序对来实现。只要序对能够用 cons、car 和 cdr 进行操作,序对的具体实现细节对有理数包的其余部分来说就无关紧要。实际上,每个层次上的过程就是定义抽象屏障并连接各层次的接口。

图 2.1:有理数包中的数据抽象屏障。

This simple idea has many advantages. One advantage is that it makes programs

much easier to maintain and to modify. Any complex data structure can be

represented in a variety of ways with the primitive data structures provided by

a programming language. Of course, the choice of representation influences the

programs that operate on it; thus, if the representation were to be changed at

some later time, all such programs might have to be modified accordingly. This

task could be time-consuming and expensive in the case of large programs unless

the dependence on the representation were to be confined by design to a very

few program modules.

这个简单的思想具有许多优点。其中一个优点是它使程序更易于维护和修改。任何复杂的数据结构都可以用程序设计语言提供的基本数据结构以多种方式来表示。当然,表示方式的选择会影响到操作它的程序;因此,如果表示方式在日后发生变化,所有相关程序都可能需要随之修改。在大型程序中,这项工作可能既费时又费力,除非在设计时就将对表示方式的依赖限制在极少数程序模块中。

For example, an alternate way to address the problem of reducing rational

numbers to lowest terms is to perform the reduction whenever we access the

parts of a rational number, rather than when we construct it. This leads to

different constructor and selector procedures:

例如,将有理数化简为最简形式的另一种做法是:在访问有理数各部分时才进行化简,而不是在构造时进行。这样就引出了不同的构造函数和选择函数过程:

The difference between this implementation and the previous one lies in when we

compute the gcd. If in our typical use of rational numbers we access

the numerators and denominators of the same rational numbers many times, it

would be preferable to compute the gcd when the rational numbers are

constructed. If not, we may be better off waiting until access time to compute

the gcd. In any case, when we change from one representation to the

other, the procedures add-rat, sub-rat, and so on do not have to

be modified at all.

这种实现与之前实现的区别在于何时计算 gcd。如果在典型使用中我们多次访问同一个有理数的分子和分母,那么在构造时就计算 gcd 会更合适。如果不是这种情况,则最好等到访问时再计算 gcd。无论如何,当我们从一种表示方式切换到另一种时,过程 add-rat、sub-rat 等根本不需要做任何修改。

Constraining the dependence on the representation to a few interface procedures

helps us design programs as well as modify them, because it allows us to

maintain the flexibility to consider alternate implementations. To continue

with our simple example, suppose we are designing a rational-number package and

we can’t decide initially whether to perform the gcd at construction

time or at selection time. The data-abstraction methodology gives us a way to

defer that decision without losing the ability to make progress on the rest of

the system.

将对表示方式的依赖限制在少数接口过程中,不仅有助于程序的设计,也便于修改,因为这使我们保有考虑替代实现方案的灵活性。继续以我们的简单例子来说明:假设我们正在设计一个有理数包,最初无法决定究竟在构造时还是在选取时计算 gcd。数据抽象方法论为我们提供了一种推迟这一决策的方式,同时不妨碍在系统其余部分取得进展。

Racket #lang sicp
(define (make-rat n d)
 (cons n d))

(define (numer x)
 (let ((g (gcd (car x) (cdr x))))
 (/ (car x) g)))

(define (denom x)
 (let ((g (gcd (car x) (cdr x))))
 (/ (cdr x) g)))