This section presents a simple language for drawing pictures that illustrates
the power of data abstraction and closure, and also exploits higher-order
procedures in an essential way. The language is designed to make it easy to
experiment with patterns such as the ones in Figure 2.9, which are
composed of repeated elements that are shifted and scaled. In this language, the data
objects being combined are represented as procedures rather than as list
structure. Just as cons, which satisfies the closure property, allowed
us to easily build arbitrarily complicated list structure, the operations in
this language, which also satisfy the closure property, allow us to easily
build arbitrarily complicated patterns.
Figure 2.9: Designs generated with the picture language.
Subheading: The picture language
本节介绍一种用于绘图的简单语言,以此阐明数据抽象 (data abstraction) 与闭包性质的威力,并以本质性的方式运用了高阶过程 (higher-order procedures)。这门语言的设计目标是便于试验如图 2.9 所示的各种图案——这些图案由反复移位、缩放的元素组合而成。在该语言中,被组合的数据对象以过程而非表结构的形式表示。正如满足闭包性质的 cons 使我们得以轻松构造任意复杂的表结构,这门语言中同样满足闭包性质的各种操作,也使我们得以轻松构造任意复杂的图案。
图 2.9:用图像语言生成的图案设计。
小节标题:图像语言
When we began our study of programming in 1.1, we emphasized the
importance of describing a language by focusing on the language’s primitives,
its means of combination, and its means of abstraction. We’ll follow that
framework here.
在 1.1 节开始研究程序设计时,我们强调了描述一门语言应关注三个要素:语言的基本表达式 (primitives)、组合的方法 (means of combination) 以及抽象的方法 (means of abstraction)。我们在此也将沿用这一框架。
Part of the elegance of this picture language is that there is only one kind of
element, called a
painter. A painter draws an image that is shifted
and scaled to fit within a designated parallelogram-shaped frame. For example,
there’s a primitive painter we’ll call wave that makes a crude line
drawing, as shown in Figure 2.10. The actual shape of the drawing
depends on the frame—all four images in figure 2.10 are produced by the
same wave painter, but with respect to four different frames. Painters
can be more elaborate than this: The primitive painter called rogers
paints a picture of MIT’s founder, William Barton Rogers, as shown in
Figure 2.11. The four images in figure 2.11 are drawn with respect to the same four
frames as the wave images in figure 2.10.
这门图像语言的优雅之处在于,它只有一种元素,称为画家 (painter)。一个画家能绘制一幅图像,该图像经移位和缩放后恰好填满指定的平行四边形形状的框架 (frame)。例如,有一个基本画家 (primitive painter) 我们称之为 wave,它绘制一幅粗糙的线条画,如图 2.10 所示。具体的图形形状取决于所用的框架——图 2.10 中的四幅图像均由同一个 wave 画家绘制,但分别对应四个不同的框架。画家可以比这更为精细:名为 rogers 的基本画家绘制的是 MIT 创始人威廉·巴顿·罗杰斯 (William Barton Rogers) 的肖像,如图 2.11 所示。图 2.11 中的四幅图像是以与图 2.10 中 wave 图像相同的四个框架为基础绘制的。
Figure 2.10: Images produced by the wave painter, with respect to four different frames. The frames, shown with dotted lines, are not part of the images.
图 2.10:wave 画家相对于四个不同框架所生成的图像。框架以虚线表示,不属于图像的组成部分。
Figure 2.11: Images of William Barton Rogers, founder and first president of MIT, painted with respect to the same four frames as in Figure 2.10 (original image from Wikimedia Commons).
图 2.11:MIT 创始人兼首任校长威廉·巴顿·罗杰斯的肖像,以与图 2.10 相同的四个框架为基础绘制(原图来自维基共享资源)。
To combine images, we use various operations that construct new painters from
given painters. For example, the beside operation takes two painters
and produces a new, compound painter that draws the first painter’s image in
the left half of the frame and the second painter’s image in the right half of
the frame. Similarly, below takes two painters and produces a compound
painter that draws the first painter’s image below the second painter’s image.
Some operations transform a single painter to produce a new painter. For
example, flip-vert takes a painter and produces a painter that draws its
image upside-down, and flip-horiz produces a painter that draws the
original painter’s image left-to-right reversed.
为了组合图像,我们使用各种操作从已有的画家构造新画家。例如,beside 操作接受两个画家,生成一个新的复合画家,该画家将第一个画家的图像绘制在框架的左半部分,将第二个画家的图像绘制在框架的右半部分。类似地,below 接受两个画家,生成一个复合画家,将第一个画家的图像绘制在第二个画家图像的下方。某些操作对单个画家进行变换以生成新画家。例如,flip-vert 接受一个画家,生成一个将其图像上下翻转绘制的画家;flip-horiz 则生成一个将原画家图像左右镜像绘制的画家。
Figure 2.12 shows the drawing of a painter called
wave4 that is built up in two stages starting from wave:
图 2.12 展示了名为 wave4 的画家的绘制结果,它从 wave 出发,经过两个阶段构建而成:
Figure 2.12: Creating a complex figure, starting from the wave painter of Figure 2.10.
图 2.12:从图 2.10 的 wave 画家出发,逐步构造复杂图形的过程。
In building up a complex image in this manner we are exploiting the fact that
painters are closed under the language’s means of combination. The
beside or below of two painters is itself a painter; therefore,
we can use it as an element in making more complex painters. As with building
up list structure using cons, the closure of our data under the means of
combination is crucial to the ability to create complex structures while using
only a few operations.
以这种方式构建复杂图像,正是利用了画家在该语言的组合方法下是封闭的这一事实。两个画家经 beside 或 below 组合后,其结果本身仍是一个画家;因此,我们可以将其用作构造更复杂画家的元素。如同用 cons 构建表结构,数据在组合方法下的封闭性对于仅凭少数几种操作就能创造复杂结构的能力至关重要。
Once we can combine painters, we would like to be able to abstract typical
patterns of combining painters. We will implement the painter operations as
Scheme procedures. This means that we don’t need a special abstraction
mechanism in the picture language: Since the means of combination are ordinary
Scheme procedures, we automatically have the capability to do anything with
painter operations that we can do with procedures. For example, we can
abstract the pattern in wave4 as
一旦能够组合画家,我们便希望对组合画家的典型模式加以抽象。我们将以 Scheme 过程的形式实现各种画家操作。这意味着在图像语言中无需专门的抽象机制:由于组合的方法就是普通的 Scheme 过程,我们自然拥有对画家操作执行任何过程可做之事的能力。例如,我们可以将 wave4 中的模式抽象为
and define wave4 as an instance of this pattern:
并将 wave4 定义为该模式的一个实例:
We can also define recursive operations. Here’s one that makes painters split
and branch towards the right as shown in Figure 2.13
and Figure 2.14:
我们还可以定义递归操作。下面是一个让画家向右分裂并分叉的操作,效果如图 2.13 和图 2.14 所示:
Figure 2.13: Recursive plans for right-split and corner-split.
图 2.13:right-split 与 corner-split 的递归构造方案。
We can produce balanced patterns by branching upwards as well as towards the
right (see Exercise 2.44, Figure 2.13 and Figure 2.14):
通过同时向上和向右分叉,我们可以生成均衡的图案(参见练习 2.44、图 2.13 和图 2.14):
Figure 2.14: The recursive operations right-split and corner-split applied to the painters wave and rogers. Combining four corner-split figures produces symmetric square-limit designs as shown in Figure 2.9.
图 2.14:将递归操作 right-split 和 corner-split 作用于画家 wave 和 rogers 的结果。将四个 corner-split 图形适当拼合,可得到如图 2.9 所示的对称的正方形极限图案。
By placing four copies of a corner-split appropriately, we obtain a
pattern called square-limit, whose application to wave and
rogers is shown in Figure 2.9:
通过将四个 corner-split 的副本适当排列,我们得到一种称为 square-limit 的图案,其作用于 wave 和 rogers 的效果如图 2.9 所示:
(define wave2 (beside wave (flip-vert wave)))
(define wave4 (below wave2 wave2)) (define (flipped-pairs painter)
(let ((painter2
(beside painter
(flip-vert painter))))
(below painter2 painter2))) (define wave4 (flipped-pairs wave)) (define (right-split painter n)
(if (= n 0)
painter
(let ((smaller (right-split painter
(- n 1))))
(beside painter
(below smaller smaller))))) (define (corner-split painter n)
(if (= n 0)
painter
(let ((up (up-split painter (- n 1)))
(right (right-split painter
(- n 1))))
(let ((top-left (beside up up))
(bottom-right (below right
right))
(corner (corner-split painter
(- n 1))))
(beside (below painter top-left)
(below bottom-right
corner)))))) (define (square-limit painter n)
(let ((quarter (corner-split painter n)))
(let ((half (beside (flip-horiz quarter)
quarter)))
(below (flip-vert half) half))))