← 学习库 Think Python (2e) · 中英对照 目录

Chapter 4  Case study: interface design 第 4 章 案例研究:接口设计

本页译自 Think Python 2e(Allen B. Downey)· Chapter 4 Case study: interface design。代码块保留英文原文不翻译;正文段段对照,中文块可用右下角按钮隐藏。

Code examples from this chapter are available from http://thinkpython.com/code/polygon.py.

本章的代码示例可以从 http://thinkpython.com/code/polygon.py 获取。

4.1 TurtleWorld 4.1 TurtleWorld(海龟世界)

To accompany this book, I have written a package called Swampy. You can download Swampy from http://thinkpython.com/swampy; follow the instructions there to install Swampy on your system.

为配合本书,我写了一个名为 Swampy 的包。你可以从 http://thinkpython.com/swampy 下载它,按那里的说明把 Swampy 装到自己的系统上。

A package is a collection of modules; one of the modules in Swampy is TurtleWorld, which provides a set of functions for drawing lines by steering turtles around the screen.

(package)就是一组模块的集合。Swampy 里有个模块叫 TurtleWorld,它提供了一套函数,让你通过驱赶海龟在屏幕上走动来画线。

If Swampy is installed as a package on your system, you can import TurtleWorld like this:

如果 Swampy 是以包的形式装在系统里的,就可以这样导入 TurtleWorld
from swampy.TurtleWorld import *

If you downloaded the Swampy modules but did not install them as a package, you can either work in the directory that contains the Swampy files, or add that directory to Python’s search path. Then you can import TurtleWorld like this:

如果你只是下载了 Swampy 的模块文件,并没有把它们装成包,那么要么直接在存放 Swampy 文件的目录里工作,要么把该目录加入 Python 的搜索路径。然后这样导入 TurtleWorld
from TurtleWorld import *

The details of the installation process and setting Python’s search path depend on your system, so rather than include those details here, I will try to maintain current information for several systems at http://thinkpython.com/swampy

安装过程和设置 Python 搜索路径的具体做法因系统而异,所以这里不展开细节;我会尽量在 http://thinkpython.com/swampy 上维护几种常见系统的最新说明。

Create a file named mypolygon.py and type in the following code:

新建一个文件 mypolygon.py,输入下面的代码:
from swampy.TurtleWorld import *

world = TurtleWorld()
bob = Turtle()
print bob

wait_for_user()

The first line imports everything from the TurtleWorld module in the swampy package.

第一行把 swampy 包中 swampy024 模块里的所有东西都导入进来。

The next lines create a TurtleWorld assigned to world and a Turtle assigned to bob. Printing bob yields something like:

接下来几行创建了一个 TurtleWorld 赋给 world,一个 Turtle 赋给 bob。打印 bob 会得到类似这样的结果:
swampy031

This means that bob refers to an instance of a Turtle as defined in module swampy033. In this context, “instance” means a member of a set; this Turtle is one of the set of possible Turtles.

这说明 bob 指向一个 Turtle 的实例(instance),Turtle 这个类型是在 swampy035 模块里定义的。此处「实例」的意思是某个集合中的一员:这只海龟是所有可能的海龟中的一只。

swampy036 tells TurtleWorld to wait for the user to do something, although in this case there’s not much for the user to do except close the window.

swampy037 让 TurtleWorld 等着用户做点什么——不过眼下用户除了关掉窗口也没别的可做。

TurtleWorld provides several turtle-steering functions: fd and fd for forward and backward, and fd and fd for left and right turns. Also, each Turtle is holding a pen, which is either down or up; if the pen is down, the Turtle leaves a trail when it moves. The functions fd and fd stand for “pen up” and “pen down.”

TurtleWorld 提供了几个驱赶海龟的函数:fdfd 表示前进和后退,fdfd 表示左转和右转。另外每只海龟都握着一支笔,笔的状态或落下或抬起;笔落下时,海龟移动就会留下痕迹。函数 fdfd 分别代表「抬笔」(pen up)和「落笔」(pen down)。

To draw a right angle, add these lines to the program (after creating bob and before calling swampy051):

要画一个直角,把下面几行加进程序(放在创建 bob 之后、调用 swampy053 之前):
swampy054

The first line tells bob to take 100 steps forward. The second line tells him to turn left.

第一行让 bob 向前走 100 步,第二行让它左转。

When you run this program, you should see bob move east and then north, leaving two line segments behind.

运行这个程序,你应该看到 bob 先向东走,再向北走,身后留下两段线段。

Now modify the program to draw a square. Don’t go on until you’ve got it working!

现在改一改程序,让它画一个正方形。没做出来就别往下看!

4.2 Simple repetition 4.2 简单的重复

Chances are you wrote something like this (leaving out the code that creates TurtleWorld and waits for the user):

你大概写出了类似下面的代码(省略了创建 TurtleWorld 和等待用户的部分):
swampy059

We can do the same thing more concisely with a bob statement. Add this example to swampy061 and run it again:

bob 语句能把同样的事情写得更简洁。把下面这个例子加到 swampy063 里再运行一遍:
swampy064

You should see something like this:

你会看到这样的输出:
swampy065

This is the simplest use of the bob statement; we will see more later. But that should be enough to let you rewrite your square-drawing program. Don’t go on until you do.

这是 bob 语句最简单的用法,后面还会见到更多。但眼下这点知识足够让你重写画正方形的程序了。改完再往下看。

Here is a bob statement that draws a square:

下面这个 bob 语句能画出一个正方形:
swampy070

The syntax of a bob statement is similar to a function definition. It has a header that ends with a colon and an indented body. The body can contain any number of statements.

bob 语句的语法和函数定义很像:有一个以冒号结尾的头部,还有一段缩进的主体。主体里可以放任意多条语句。

A bob statement is sometimes called a loop because the flow of execution runs through the body and then loops back to the top. In this case, it runs the body four times.

bob 语句有时被称作循环(loop),因为执行流走完主体后又绕回顶部。在这个例子里,主体执行了四次。

This version is actually a little different from the previous square-drawing code because it makes another turn after drawing the last side of the square. The extra turn takes a little more time, but it simplifies the code if we do the same thing every time through the loop. This version also has the effect of leaving the turtle back in the starting position, facing in the starting direction.

这个版本和前面画正方形的代码略有不同:它在画完最后一条边之后还会再转一次弯。多转这一下要多花点时间,但循环每轮都做同样的事,代码反而更简单。这个版本还有个好处:海龟最后回到起点,朝向也和出发时一致。

4.3 Exercises 4.3 习题

The following is a series of exercises using TurtleWorld. They are meant to be fun, but they have a point, too. While you are working on them, think about what the point is.

下面是一组用 TurtleWorld 做的练习。它们是为了好玩,但也各有用意。做的时候想一想,用意何在。

The following sections have solutions to the exercises, so don’t look until you have finished (or at least tried).

后面几节给出了这些习题的解答,所以做完(或至少认真试过)之前别偷看。
  1. Write a function called swampy that takes a parameter named t, which is a turtle. It should use the turtle to draw a square. Write a function call that passes bob as an argument to swampy, and then run the program again.
  2. Add another parameter, named swampy, to swampy. Modify the body so length of the sides is swampy, and then modify the function call to provide a second argument. Run the program again. Test your program with a range of values for swampy.
  3. The functions fd and fd make 90-degree turns by default, but you can provide a second argument that specifies the number of degrees. For example, swampy085 turns bob 45 degrees to the left. Make a copy of swampy and change the name to swampy0. Add another parameter named t and modify the body so it draws an n-sided regular polygon. Hint: The exterior angles of an n-sided regular polygon are 360/n degrees.
  4. Write a function called swampy that takes a turtle, t, and radius, t, as parameters and that draws an approximate circle by invoking swampy0 with an appropriate length and number of sides. Test your function with a range of values of t. Hint: figure out the circumference of the circle and make sure that swampy095. Another hint: if bob is too slow for you, you can speed him up by changing swampy097, which is the time between moves, in seconds. swampy098 ought to get him moving.
  5. Make a more general version of swampy called bob that takes an additional parameter world, which determines what fraction of a circle to draw. world is in units of degrees, so when swampy103, bob should draw a complete circle.
  1. 写一个名为 swampy 的函数,它接受一个形参 t,即一只海龟。函数应当用这只海龟画出一个正方形。 再写一个函数调用,把 bob 作为实参传给 swampy,然后重新运行程序。
  2. swampy 再加一个形参 swampy。修改函数体,让边长等于 swampy,再修改函数调用,补上第二个实参。重新运行程序,用一系列不同的 swampy 取值测试。
  3. 函数 fdfd 默认转 90 度,但你可以给第二个实参指定角度。例如 swampy115 让 bob 左转 45 度。 把 swampy 复制一份,改名为 swampy1。再加一个形参 t,修改函数体,让它画出一个 n 边的正多边形。提示:n 边正多边形的外角是 360/n 度。
  4. 写一个名为 swampy 的函数,接受一只海龟 t 和半径 t 作为形参,通过给 swampy1 传入合适的边长与边数来画一个近似的圆。用一系列不同的 t 取值测试你的函数。 提示:先算出圆的周长,并保证 swampy125。 再提示一句:如果嫌 bob 太慢,可以改 swampy127 给它提速,这个值是两次移动之间的间隔,单位为秒。swampy128 应该能让它跑起来。
  5. swampy 改造成更通用的版本,叫 bob,多加一个形参 world,用来决定画圆的多大一部分。world 以度为单位,因此当 swampy133 时,bob 应当画出一整个圆。

4.4 Encapsulation 4.4 封装

The first exercise asks you to put your square-drawing code into a function definition and then call the function, passing the turtle as a parameter. Here is a solution:

第一道习题要求你把画正方形的代码放进一个函数定义里,然后调用这个函数,把海龟当作形参传进去。下面是一种解法:
swampy135

The innermost statements, fd and fd are indented twice to show that they are inside the bob loop, which is inside the function definition. The next line, swampy139, is flush with the left margin, so that is the end of both the bob loop and the function definition.

最内层的语句 fdfd 缩进了两层,表示它们在 bob 循环里,而循环又在函数定义里。下一行 swampy144 顶到最左边,因此 bob 循环和函数定义在此同时结束。

Inside the function, t refers to the same turtle bob refers to, so world has the same effect as swampy1. So why not call the parameter bob? The idea is that t can be any turtle, not just bob, so you could create a second turtle and pass it as an argument to swampy:

在函数内部,tbob 指向同一只海龟,所以 worldswampy1 效果相同。那为什么不干脆把形参就叫 bob 呢?因为 t 可以是任意一只海龟,不限于 bob:你完全可以再造一只海龟,把它作为实参传给 swampy
swampy162

Wrapping a piece of code up in a function is called encapsulation. One of the benefits of encapsulation is that it attaches a name to the code, which serves as a kind of documentation. Another advantage is that if you re-use the code, it is more concise to call a function twice than to copy and paste the body!

把一段代码包进函数里,这叫做封装(encapsulation)。封装的好处之一是给这段代码取了个名字,名字本身就是一种文档。另一个好处是复用时更省事:调用函数两次,总比把函数体复制粘贴两遍来得干净。

4.5 Generalization 4.5 泛化

The next step is to add a swampy parameter to swampy. Here is a solution:

下一步是给 swampy 加一个 swampy 形参。下面是一种解法:
swampy167

Adding a parameter to a function is called generalization because it makes the function more general: in the previous version, the square is always the same size; in this version it can be any size.

给函数增加形参叫做泛化(generalization),因为这让函数更通用了:上一个版本画出的正方形大小固定,这个版本可以画任意大小。

The next step is also a generalization. Instead of drawing squares, swampy1 draws regular polygons with any number of sides. Here is a solution:

再下一步同样是泛化。swampy1 不再局限于正方形,而是能画任意边数的正多边形。下面是一种解法:
swampy170

This draws a 7-sided polygon with side length 70. If you have more than a few numeric arguments, it is easy to forget what they are, or what order they should be in. It is legal, and sometimes helpful, to include the names of the parameters in the argument list:

这会画出一个边长为 70 的七边形。数值实参一多,就容易忘记哪个是什么、该按什么顺序排。把形参名字写进实参列表是合法的,有时也很有用:
swampy171

These are called keyword arguments because they include the parameter names as “keywords” (not to be confused with Python keywords like world and bob).

这叫做关键字实参(keyword argument),因为其中把形参名当作「关键字」写了出来(别和 worldbob 这类 Python 关键字混为一谈)。

This syntax makes the program more readable. It is also a reminder about how arguments and parameters work: when you call a function, the arguments are assigned to the parameters.

这种写法让程序更易读,也提醒着实参与形参的工作方式:调用函数时,实参会赋值给形参。

4.6 Interface design 4.6 接口设计

The next step is to write swampy, which takes a radius, t, as a parameter. Here is a simple solution that uses swampy1 to draw a 50-sided polygon:

下一步是写 swampy,它接受半径 t 作为形参。下面是一个简单解法,用 swampy1 画一个五十边形:
swampy182

The first line computes the circumference of a circle with radius t using the formula 2 π r. Since we use swampy1, we have to import bob0. By convention, swampy statements are usually at the beginning of the script.

第一行用 2 π r 算出半径为 t 的圆的周长。既然用到 swampy1,就得导入 bob0。按惯例,swampy 语句一般放在脚本开头。

t is the number of line segments in our approximation of a circle, so swampy is the length of each segment. Thus, swampy1 draws a 50-sides polygon that approximates a circle with radius t.

t 是我们用来近似圆的线段条数,swampy 就是每段的长度。于是 swampy1 画出一个五十边形,近似半径为 t 的圆。

One limitation of this solution is that t is a constant, which means that for very big circles, the line segments are too long, and for small circles, we waste time drawing very small segments. One solution would be to generalize the function by taking t as a parameter. This would give the user (whoever calls swampy) more control, but the interface would be less clean.

这个解法有个局限:t 是常量。圆很大时线段太长,圆很小时又浪费时间去画一堆极短的线段。一个办法是把 t 也变成形参,从而泛化这个函数。这样用户(也就是调用 swampy 的人)能有更多控制权,但接口就不那么清爽了。

The interface of a function is a summary of how it is used: what are the parameters? What does the function do? And what is the return value? An interface is “clean” if it is “as simple as possible, but not simpler. (Einstein)”

函数的接口(interface)是对其用法的概括:有哪些形参?函数做什么?返回值是什么?一个接口称得上「清爽」,就得做到「尽可能简单,但不能过于简单」(爱因斯坦语)。

In this example, t belongs in the interface because it specifies the circle to be drawn. t is less appropriate because it pertains to the details of how the circle should be rendered.

在这个例子里,t 理应放进接口,因为它确定了要画哪个圆。t 就不太合适,它涉及的是圆如何绘制的细节。

Rather than clutter up the interface, it is better to choose an appropriate value of t depending on swampy210:

与其把接口搞乱,不如根据 swampy211 自行选一个合适的 t
swampy213

Now the number of segments is (approximately) swampy214, so the length of each segment is (approximately) 3, which is small enough that the circles look good, but big enough to be efficient, and appropriate for any size circle.

现在线段数大约是 swampy215,每段长度大约是 3——小到足以让圆看起来光滑,又大到不失效率,而且对任意大小的圆都合适。

4.7 Refactoring 4.7 重构

When I wrote swampy, I was able to re-use swampy2 because a many-sided polygon is a good approximation of a circle. But bob is not as cooperative; we can’t use swampy2 or swampy to draw an arc.

swampy 时我能复用 swampy2,因为边数很多的多边形足以近似一个圆。但 bob 就不这么好说话了:swampy2 和 swampy 都画不出圆弧。

One alternative is to start with a copy of swampy2 and transform it into bob. The result might look like this:

一个办法是把 swampy2 复制一份,再改造成 bob。结果大概是这样:
swampy230

The second half of this function looks like swampy2, but we can’t re-use swampy2 without changing the interface. We could generalize swampy2 to take an angle as a third argument, but then swampy2 would no longer be an appropriate name! Instead, let’s call the more general function swampy23:

这个函数的后半段和 swampy2 很像,但不改接口就没法复用 swampy2。我们可以把 swampy2 泛化,让它接受一个角度作为第三个实参,可这么一来 swampy2 这名字就不合适了!干脆把这个更通用的函数叫 swampy24:
swampy241

Now we can rewrite swampy2 and bob to use swampy24:

现在可以重写 swampy2 和 bob,让它们都调用 swampy24:
swampy248

Finally, we can rewrite swampy to use bob:

最后,还可以重写 swampy,让它调用 bob
swampy253

This process—rearranging a program to improve function interfaces and facilitate code re-use—is called refactoring. In this case, we noticed that there was similar code in bob and swampy2, so we “factored it out” into swampy25.

这个过程——重新组织程序以改进函数接口、便利代码复用——叫做重构(refactoring)。在这个例子里,我们发现 bobswampy2 里有相似的代码,就把它「提取」到了 swampy25 中。

If we had planned ahead, we might have written swampy26 first and avoided refactoring, but often you don’t know enough at the beginning of a project to design all the interfaces. Once you start coding, you understand the problem better. Sometimes refactoring is a sign that you have learned something.

要是早有筹划,我们大可以先写 swampy26,省去重构这一步。但项目开头往往了解得不够,没法把所有接口都设计好。一旦动手写代码,对问题的理解才会加深。有时候,需要重构恰恰说明你学到了新东西。

4.8 A development plan 4.8 一种开发方案

A development plan is a process for writing programs. The process we used in this case study is “encapsulation and generalization.” The steps of this process are:

开发方案(development plan)是编写程序的一套流程。本章案例研究用的流程叫「封装与泛化」,步骤如下:
  1. Start by writing a small program with no function definitions.
  2. Once you get the program working, encapsulate it in a function and give it a name.
  3. Generalize the function by adding appropriate parameters.
  4. Repeat steps 1–3 until you have a set of working functions. Copy and paste working code to avoid retyping (and re-debugging).
  5. Look for opportunities to improve the program by refactoring. For example, if you have similar code in several places, consider factoring it into an appropriately general function.
  1. 先写一个不含任何函数定义的小程序。
  2. 程序跑通之后,把它封装成一个函数,并起个名字。
  3. 加上合适的形参,把函数泛化。
  4. 重复第 1–3 步,直到攒出一组能用的函数。可以复制粘贴已经能跑的代码,免得重新敲一遍(还得重新调试一遍)。
  5. 留意可以通过重构改进程序的机会。比如几处地方有相似的代码,就考虑把它提取成一个足够通用的函数。

This process has some drawbacks—we will see alternatives later—but it can be useful if you don’t know ahead of time how to divide the program into functions. This approach lets you design as you go along.

这个流程也有缺点——后面会介绍别的方案——但当你事先不知道该怎么把程序拆成函数时,它很管用:可以边写边设计。

4.9 docstring 4.9 文档字符串

A docstring is a string at the beginning of a function that explains the interface (“doc” is short for “documentation”). Here is an example:

文档字符串(docstring)是放在函数开头、用来说明接口的字符串(「doc」是「documentation」的缩写)。举个例子:
swampy262

This docstring is a triple-quoted string, also known as a multiline string because the triple quotes allow the string to span more than one line.

这个文档字符串用三重引号括起来,也叫多行字符串——三重引号允许字符串跨越多行。

It is terse, but it contains the essential information someone would need to use this function. It explains concisely what the function does (without getting into the details of how it does it). It explains what effect each parameter has on the behavior of the function and what type each parameter should be (if it is not obvious).

它很简短,但包含了别人使用这个函数所需的关键信息:简明地说清函数做什么(不牵扯它怎么做),说清每个形参对函数行为有何影响,以及每个形参应当是什么类型(如果不够显然的话)。

Writing this kind of documentation is an important part of interface design. A well-designed interface should be simple to explain; if you are having a hard time explaining one of your functions, that might be a sign that the interface could be improved.

写这类文档是接口设计的重要一环。设计良好的接口应当容易讲清楚;如果某个函数你怎么说都说不明白,那也许说明接口还有改进空间。

4.10 Debugging 4.10 调试

An interface is like a contract between a function and a caller. The caller agrees to provide certain parameters and the function agrees to do certain work.

接口就像函数与调用者之间的一份合约:调用者答应提供若干参数,函数答应完成某些工作。

For example, swampy26 requires four arguments: t has to be a Turtle; t is the number of line segments, so it has to be an integer; swampy should be a positive number; and world has to be a number, which is understood to be in degrees.

例如 swampy26 需要四个实参:t 必须是一只 Turtle;t 是线段条数,所以必须是整数;swampy 应当是正数;world 必须是数字,且默认以度为单位。

These requirements are called preconditions because they are supposed to be true before the function starts executing. Conversely, conditions at the end of the function are postconditions. Postconditions include the intended effect of the function (like drawing line segments) and any side effects (like moving the Turtle or making other changes in the World).

这些要求称为前置条件(preconditions),因为它们应当在函数开始执行之前就成立。相应地,函数结束时应当成立的条件叫后置条件(postconditions)。后置条件包括函数的预期效果(比如画出线段)和一切副作用(比如移动了海龟,或改变了 World 里的其他状态)。

Preconditions are the responsibility of the caller. If the caller violates a (properly documented!) precondition and the function doesn’t work correctly, the bug is in the caller, not the function.

前置条件由调用者负责。如果调用者违反了(而且文档写得清清楚楚的)前置条件,导致函数工作不正常,那缺陷在调用者身上,不在函数身上。

4.11 Glossary 4.11 术语表

instance:
A member of a set. The TurtleWorld in this chapter is a member of the set of TurtleWorlds.
loop:
A part of a program that can execute repeatedly.
encapsulation:
The process of transforming a sequence of statements into a function definition.
generalization:
The process of replacing something unnecessarily specific (like a number) with something appropriately general (like a variable or parameter).
keyword argument:
An argument that includes the name of the parameter as a “keyword.”
interface:
A description of how to use a function, including the name and descriptions of the arguments and return value.
refactoring:
The process of modifying a working program to improve function interfaces and other qualities of the code.
development plan:
A process for writing programs.
docstring:
A string that appears in a function definition to document the function’s interface.
precondition:
A requirement that should be satisfied by the caller before a function starts.
postcondition:
A requirement that should be satisfied by the function before it ends.
instance 实例:
集合中的一员。本章里的 TurtleWorld 就是所有 TurtleWorld 组成的集合中的一员。
loop 循环:
程序中可以反复执行的一部分。
encapsulation 封装:
把一串语句变成一个函数定义的过程。
generalization 泛化:
把不必要地具体的东西(比如一个数字)换成恰当通用的东西(比如一个变量或形参)的过程。
keyword argument 关键字实参:
把形参名字当作「关键字」一并写出的实参。
interface 接口:
对一个函数用法的描述,包括名称、各实参的说明以及返回值的说明。
refactoring 重构:
修改一个能正常工作的程序,以改进函数接口和代码的其他品质的过程。
development plan 开发方案:
编写程序的一套流程。
docstring 文档字符串:
出现在函数定义中、用于记录该函数接口的字符串。
precondition 前置条件:
函数开始执行之前应由调用者满足的要求。
postcondition 后置条件:
函数结束之前应由函数自身满足的要求。

4.12 Exercises 4.12 习题

Exercise 1

习题 1

Download the code in this chapter from swampy273.

swampy274 下载本章的代码。
  1. Write appropriate docstrings for swampy2, bob and swampy.
  2. Draw a stack diagram that shows the state of the program while executing swampy278. You can do the arithmetic by hand or add world statements to the code.
  3. The version of bob in Section 4.7 is not very accurate because the linear approximation of the circle is always outside the true circle. As a result, the turtle ends up a few units away from the correct destination. My solution shows a way to reduce the effect of this error. Read the code and see if it makes sense to you. If you draw a diagram, you might see how it works.
  1. swampy2、bobswampy 写上合适的文档字符串。
  2. 画一张栈图,展示执行 swampy284 时程序的状态。算术可以手算,也可以在代码里加 world 语句。
  3. 4.7 节里的 bob 版本不太精确,因为用线段近似圆时,折线始终落在真实圆的外侧。结果海龟最终会偏离正确终点几个单位。我给出的解答展示了一种减小这一误差的办法。读一读代码,看能不能看懂。画张图或许就明白它的原理了。

Figure 4.1: Turtle flowers.

图 4.1:海龟画的花。(原书插图未收录)

Exercise 2

习题 2

Write an appropriately general set of functions that can draw flowers as in Figure 4.1.

写一组足够通用的函数,能画出图 4.1 那样的花。

Solution: swampy287, also requires swampy288.

解答:swampy289,还需要 swampy290。

Figure 4.2: Turtle pies.

图 4.2:海龟画的派。(原书插图未收录)

Exercise 3

习题 3

Write an appropriately general set of functions that can draw shapes as in Figure 4.2.

写一组足够通用的函数,能画出图 4.2 那样的形状。

Solution: swampy291.

解答:swampy292。

Exercise 4

习题 4

The letters of the alphabet can be constructed from a moderate number of basic elements, like vertical and horizontal lines and a few curves. Design a font that can be drawn with a minimal number of basic elements and then write functions that draw letters of the alphabet.

字母表里的字母可以由不多的几种基本元素拼出来,比如竖线、横线和少量曲线。设计一套字体,让它用尽可能少的基本元素就能画出来,然后写出画各个字母的函数。

You should write one function for each letter, with names swampy, swampy, etc., and put your functions in a file named swampy295. You can download a “turtle typewriter” from swampy296 to help you test your code.

每个字母写一个函数,命名为 swampyswampy 等等,把这些函数放在名为 swampy299 的文件里。你可以从 swampy300 下载一台「海龟打字机」,用它来测试你的代码。

Solution: swampy301, also requires swampy302.

解答:swampy303,还需要 swampy304。

Exercise 5

习题 5

Read about spirals at swampy305; then write a program that draws an Archimedian spiral (or one of the other kinds). Solution: swampy306.

swampy307 读一读螺线的资料,然后写一个程序画出阿基米德螺线(或别的种类的螺线)。解答:swampy308。