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

Chapter 6  Fruitful functions 第 6 章 有返回值的函数

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

6.1 Return values 6.1 返回值

Some of the built-in functions we have used, such as the math functions, produce results. Calling the function generates a value, which we usually assign to a variable or use as part of an expression.

我们目前用过的部分内置函数(如数学函数)会产生结果。调用函数会生成一个值,我们通常把它赋给变量,或作为表达式的一部分来使用。
e = math.exp(1.0)
height = radius * math.sin(radians)

All of the functions we have written so far are void; they print something or move turtles around, but their return value is None.

到目前为止我们写过的函数都是无返回值函数;它们要么打印一些内容,要么移动海龟,但它们的返回值是 None

In this chapter, we are (finally) going to write fruitful functions. The first example is None, which returns the area of a circle with the given radius:

在本章中,我们(终于)要开始编写有返回值函数了。第一个例子是 None,它返回给定半径的圆的面积:
None00009

We have seen the None00 statement before, but in a fruitful function the None00 statement includes an expression. This statement means: "Return immediately from this function and use the following expression as a return value." The expression can be arbitrarily complicated, so we could have written this function more concisely:

我们之前见过 None00 语句,但在有返回值函数里,None00 语句包含一个表达式。这条语句的意思是:「立即从函数返回,并用后面的表达式作为返回值。」表达式可以任意复杂,所以我们本可以把这个函数写得更简洁:
None00014

On the other hand, temporary variables like None often make debugging easier.

另一方面,像 None 这样的临时变量往往能让调试更轻松。

Sometimes it is useful to have multiple return statements, one in each branch of a conditional:

有时在条件语句的每个分支里各放一条 None00 语句会很有用:
None00018

Since these None00 statements are in an alternative conditional, only one will be executed.

由于这些 None00 语句位于互斥的条件分支中,只有一条会被执行。

As soon as a return statement executes, the function terminates without executing any subsequent statements. Code that appears after a None00 statement, or any other place the flow of execution can never reach, is called dead code.

一旦 None00 语句执行,函数就会终止,不再执行任何后续语句。出现在 None00 语句之后、或执行流永远无法到达的其他位置的代码,被称为死代码。

In a fruitful function, it is a good idea to ensure that every possible path through the program hits a None00 statement. For example:

在有返回值函数中,最好确保程序中的每一条可能路径都能碰到一条 None00 语句。例如:
None00026

This function is incorrect because if x happens to be 0, neither condition is true, and the function ends without hitting a None00 statement. If the flow of execution gets to the end of a function, the return value is None, which is not the absolute value of 0.

这个函数是错的,因为如果 x 恰好等于 0,两个条件都不成立,函数会在没有碰到 None00 语句的情况下结束。如果执行流走到函数末尾,返回值就是 None,而这并不是 0 的绝对值。
None00033

By the way, Python provides a built-in function called x00 that computes absolute values.

顺带一提,Python 提供了一个名为 x00 的内置函数,用来计算绝对值。

Exercise 1

习题 1

Write a None000 function that returns x if None0003, x if None00, and x0 if None0004.

写一个 None000 函数:当 None0004 时返回 x,当 None00 时返回 x,当 None0004 时返回 x0。

6.2 Incremental development 6.2 增量开发

As you write larger functions, you might find yourself spending more time debugging.

随着函数越写越大,你可能会发现自己花在调试上的时间越来越多。

To deal with increasingly complex programs, you might want to try a process called incremental development. The goal of incremental development is to avoid long debugging sessions by adding and testing only a small amount of code at a time.

为了应对日趋复杂的程序,不妨试试一种叫增量开发的流程。增量开发的目标,是靠每次只添加并测试一小段代码,来避免长时间的调试。

As an example, suppose you want to find the distance between two points, given by the coordinates (x1, y1) and (x2, y2). By the Pythagorean theorem, the distance is:

举例来说,假设你想求两点之间的距离,这两点由坐标 (x1, y1) 和 (x2, y2) 给出。根据勾股定理,距离为:

distance = √(x2x1)2 + (y2y1)2

distance = √(x2x1)2 + (y2y1)2

The first step is to consider what a None0005 function should look like in Python. In other words, what are the inputs (parameters) and what is the output (return value)?

第一步要想清楚 None0005 函数在 Python 里该是什么样子。换句话说,输入(形参)是什么,输出(返回值)又是什么?

In this case, the inputs are two points, which you can represent using four numbers. The return value is the distance, which is a floating-point value.

本例中,输入是两个点,可以用四个数表示;返回值是一个浮点数,也就是距离。

Already you can write an outline of the function:

至此你已经能写出一个函数框架了:
None00052

Obviously, this version doesn't compute distances; it always returns zero. But it is syntactically correct, and it runs, which means that you can test it before you make it more complicated.

显然这个版本并不计算距离,它永远返回 0。但它在语法上正确、也能运行,也就是说你可以在把它变复杂之前先测试它。

To test the new function, call it with sample arguments:

要测试这个新函数,用示例实参调用它:
None00053

I chose these values so that the horizontal distance is 3 and the vertical distance is 4; that way, the result is 5 (the hypotenuse of a 3-4-5 triangle). When testing a function, it is useful to know the right answer.

我挑了这些值,使水平距离为 3、垂直距离为 4;这样结果就是 5(一个 3-4-5 三角形的斜边)。测试函数时,知道正确答案是很有用的。

At this point we have confirmed that the function is syntactically correct, and we can start adding code to the body. A reasonable next step is to find the differences x2x1 and y2y1. The next version stores those values in temporary variables and prints them.

到这一步,我们已经确认函数在语法上正确,可以开始往函数体里加代码了。一个合理的下一步是先求出 x2x1y2y1 的差。下一个版本把这些值存进临时变量并打印出来。
None00054

If the function is working, it should display None00055 and None00056. If so, we know that the function is getting the right arguments and performing the first computation correctly. If not, there are only a few lines to check.

如果函数工作正常,它应当显示 None00057 和 None00058。若是如此,我们就知道函数拿到了正确的实参,并且第一步计算无误。如果不是,需要检查的行就只剩下几行。

Next we compute the sum of squares of x0 and x0:

接下来我们计算 x0 和 x0 的平方和:
None00063

Again, you would run the program at this stage and check the output (which should be 25). Finally, you can use None00064 to compute and return the result:

同样,在这个阶段运行程序并检查输出(应当为 25)。最后,你可以用 None00065 计算并返回结果:
None00066

If that works correctly, you are done. Otherwise, you might want to print the value of None00 before the return statement.

如果结果正确,你就完成了。否则,你可能想在 None00 语句之前把 None00 的值打印出来。

The final version of the function doesn't display anything when it runs; it only returns a value. The None0 statements we wrote are useful for debugging, but once you get the function working, you should remove them. Code like that is called scaffolding because it is helpful for building the program but is not part of the final product.

函数的最终版本在运行时什么都不显示,它只返回一个值。我们写的那些 None0 语句对调试有用,但一旦函数能正常工作,就应该删掉。这类代码叫做脚手架,因为它有助于搭建程序,却不属于最终产品。

When you start out, you should add only a line or two of code at a time. As you gain more experience, you might find yourself writing and debugging bigger chunks. Either way, incremental development can save you a lot of debugging time.

刚开始时,你每次只该加一两行代码。随着经验增加,你也许能一次写完并调试更大的代码块。不管怎样,增量开发都能替你省下大量调试时间。

The key aspects of the process are:

这个流程的关键要点是:
  1. Start with a working program and make small incremental changes. At any point, if there is an error, you should have a good idea where it is.
  2. Use temporary variables to hold intermediate values so you can display and check them.
  3. Once the program is working, you might want to remove some of the scaffolding or consolidate multiple statements into compound expressions, but only if it does not make the program difficult to read.
  1. 从一个能运行的程序出发,做小的增量改动。任何时候出错,你都该大致知道错在哪里。
  2. 用临时变量保存中间值,以便显示并检查它们。
  3. 程序能正常运行后,你可以去掉部分脚手架,或把多条语句合并成复合表达式,但前提是这不会让程序难以阅读。

Exercise 2

习题 2

Use incremental development to write a function called None00072 that returns the length of the hypotenuse of a right triangle given the lengths of the two legs as arguments. Record each stage of the development process as you go.

用增量开发写一个名为 None00073 的函数,给定两条直角边的长度作为实参,返回直角三角形的斜边长度。边做边记录开发过程的每个阶段。

6.3 Composition 6.3 组合

As you should expect by now, you can call one function from within another. This ability is called composition.

正如你现在应当预料到的,你可以从一个函数内部调用另一个函数。这种能力叫做组合。

As an example, we'll write a function that takes two points, the center of the circle and a point on the perimeter, and computes the area of the circle.

举个例子,我们来写一个函数,它接收两个点——圆心和圆周上的一点——并计算这个圆的面积。

Assume that the center point is stored in the variables x0 and x0, and the perimeter point is in x0 and x0. The first step is to find the radius of the circle, which is the distance between the two points. We just wrote a function, None0007, that does that:

假设圆心存在变量 x0 和 x0 中,圆周上的点存在 x0 和 x0 中。第一步是求圆的半径,也就是两点之间的距离。我们刚写的 None0008 函数正好做这件事:
None00084

The next step is to find the area of a circle with that radius; we just wrote that, too:

接下来是求以该半径为半径的圆的面积;这个我们也刚写过:
None00085

Encapsulating these steps in a function, we get:

把这些步骤封装进一个函数,得到:
None00086

The temporary variables None00 and None00 are useful for development and debugging, but once the program is working, we can make it more concise by composing the function calls:

临时变量 None00 和 None00 对开发和调试很有用,但一旦程序能工作,我们就可以通过组合函数调用来让它更简洁:
None00091

6.4 Boolean functions 6.4 布尔函数

Functions can return booleans, which is often convenient for hiding complicated tests inside functions. For example:

函数可以返回布尔值,这常能方便地把复杂的判断藏在函数内部。例如:
None00092

It is common to give boolean functions names that sound like yes/no questions; None00093 returns either None or None0 to indicate whether x is divisible by x.

给布尔函数取听起来像是非题的名字是常见做法;None00098 返回 NoneNone0,表示 x 能否被 x 整除。

Here is an example:

下面是一个例子:
None00103

The result of the x0 operator is a boolean, so we can write the function more concisely by returning it directly:

x0 运算符的结果就是布尔值,所以我们直接把它返回,就能把函数写得更简洁:
None00106

Boolean functions are often used in conditional statements:

布尔函数常用于条件语句中:
None00107

It might be tempting to write something like:

你或许会忍不住写成这样:
None00108

But the extra comparison is unnecessary.

但这个多余的比较是不必要的。

Exercise 3

习题 3

Write a function None00109 that returns None if xyz or None0 otherwise.

写一个函数 None00112:当 xyz 时返回 None,否则返回 None0。

6.5 More recursion 6.5 更多递归

We have only covered a small subset of Python, but you might be interested to know that this subset is a complete programming language, which means that anything that can be computed can be expressed in this language. Any program ever written could be rewritten using only the language features you have learned so far (actually, you would need a few commands to control devices like the keyboard, mouse, disks, etc., but that's all).

我们目前只覆盖了 Python 的一小部分,但你可能想知道:这部分子集是一门完备的编程语言,也就是说,任何可计算的东西都能用这门语言表达。有史以来写过的任何程序,都可以只用你目前学到的语言特性重写(实际上,你还需要几条命令来控制键盘、鼠标、磁盘等设备,但仅此而已)。

Proving that claim is a nontrivial exercise first accomplished by Alan Turing, one of the first computer scientists (some would argue that he was a mathematician, but a lot of early computer scientists started as mathematicians). Accordingly, it is known as the Turing Thesis. For a more complete (and accurate) discussion of the Turing Thesis, I recommend Michael Sipser's book Introduction to the Theory of Computation.

证明这一论断是一项不小的成就,最早由艾伦·图灵——最早的计算机科学家之一——完成(有人会争辩说他是数学家,但许多早期的计算机科学家都出身于数学)。因此,它被称为图灵论题。关于图灵论题更完整(也更准确)的讨论,我推荐 Michael Sipser 的《计算理论导论》。

To give you an idea of what you can do with the tools you have learned so far, we'll evaluate a few recursively defined mathematical functions. A recursive definition is similar to a circular definition, in the sense that the definition contains a reference to the thing being defined. A truly circular definition is not very useful:

为了让你对目前学到的工具能做什么有个概念,我们来求值几个递归定义的数学函数。递归定义类似于循环定义,即定义中包含了对被定义事物本身的引用。真正的循环定义没什么用处:
vorpal:
An adjective used to describe something that is vorpal.
vorpal(原文自造词):
一个用来形容「vorpal 的事物」的形容词。

If you saw that definition in the dictionary, you might be annoyed. On the other hand, if you looked up the definition of the factorial function, denoted with the symbol !, you might get something like this:

如果你在字典里看到这个定义,可能会有点恼火。另一方面,如果你去查阶乘函数的定义(用符号 ! 表示),你可能会看到类似这样的内容:

0! = 1
n! = n(n−1)!

0! = 1
n! = n(n−1)!

This definition says that the factorial of 0 is 1, and the factorial of any other value, n, is n multiplied by the factorial of n−1.

这个定义说:0 的阶乘是 1;任何其他值 n 的阶乘,是 n 乘以 n−1 的阶乘。

So 3! is 3 times 2!, which is 2 times 1!, which is 1 times 0!. Putting it all together, 3! equals 3 times 2 times 1 times 1, which is 6.

所以 3! 等于 3 乘 2!,2! 等于 2 乘 1!,1! 等于 1 乘 0!。合在一起,3! 等于 3×2×1×1,也就是 6。

If you can write a recursive definition of something, you can usually write a Python program to evaluate it. The first step is to decide what the parameters should be. In this case it should be clear that None00115 takes an integer:

如果你能写出某个东西的递归定义,通常就能写出一个 Python 程序来求值它。第一步是决定形参应该是什么。在本例中,None00116 显然接收一个整数:
None00117

If the argument happens to be 0, all we have to do is return 1:

如果实参恰好是 0,我们只需返回 1:
None00118

Otherwise, and this is the interesting part, we have to make a recursive call to find the factorial of n−1 and then multiply it by n:

否则(这也是有趣的部分),我们必须做递归调用来求 n−1 的阶乘,然后再乘以 n
None00119

The flow of execution for this program is similar to the flow of None00120 in Section 5.8. If we call None00121 with the value 3:

这个程序的执行流与第 5.8 节 None00122 的执行流类似。如果我们用值 3 调用 None00123:

Since 3 is not 0, we take the second branch and calculate the factorial of x00...

由于 3 不等于 0,我们走第二个分支,计算 x00 的阶乘……

Since 2 is not 0, we take the second branch and calculate the factorial of x00...

由于 2 不等于 0,我们走第二个分支,计算 x00 的阶乘……

Since 0 is 0, we take the first branch and return 1 without making any more recursive calls.

由于 0 确实等于 0,我们走第一个分支,返回 1,不再做任何递归调用。

The return value (1) is multiplied by n, which is 1, and the result is returned.

返回值(1)乘以 n(即 1),得到结果并返回。

The return value (1) is multiplied by n, which is 2, and the result is returned.

返回值(1)乘以 n(即 2),得到结果并返回。

The return value (2) is multiplied by n, which is 3, and the result, 6, becomes the return value of the function call that started the whole process.

返回值(2)乘以 n(即 3),得到结果 6,它成为启动整个过程的那个函数调用的返回值。

Figure 6.1: Stack diagram.

图 6.1:栈图(原书插图未收录)

The return values are shown being passed back up the stack. In each frame, the return value is the value of None00, which is the product of x and None001.

图中显示了返回值如何沿栈向上回传。在每一帧中,返回值就是 None00 的值,即 xNone001 的乘积。

In the last frame, the local variables None001 and None00 do not exist, because the branch that creates them does not execute.

在最后一帧中,局部变量 None001 和 None00 并不存在,因为创建它们的那个分支没有执行。

6.6 Leap of faith 6.6 信仰之跃

Following the flow of execution is one way to read programs, but it can quickly become labyrinthine. An alternative is what I call the "leap of faith." When you come to a function call, instead of following the flow of execution, you assume that the function works correctly and returns the right result.

顺着执行流读程序是一种方式,但它很快会变得像迷宫一样。另一种方式是我所谓的「信仰之跃」。当你遇到一次函数调用时,不要顺着执行流走,而是假定这个函数能正确工作、返回正确的结果。

In fact, you are already practicing this leap of faith when you use built-in functions. When you call None0013 or None0013, you don't examine the bodies of those functions. You just assume that they work because the people who wrote the built-in functions were good programmers.

事实上,当你使用内置函数时就已经在实践这种信仰之跃了。当你调用 None0014 或 None0014 时,你并不会去翻看那些函数的内部。你只是假定它们能用,因为写这些内置函数的人都是好程序员。

The same is true when you call one of your own functions. For example, in Section 6.4, we wrote a function called None00142 that determines whether one number is divisible by another. Once we have convinced ourselves that this function is correct—by examining the code and testing—we can use the function without looking at the body again.

调用你自己写的函数时也一样。例如,在第 6.4 节我们写过一个 None00143 函数,用来判断一个数能否被另一个数整除。一旦你通过检查代码和测试确信这个函数是对的,就可以不再去看它的函数体而直接使用。

The same is true of recursive programs. When you get to the recursive call, instead of following the flow of execution, you should assume that the recursive call works (yields the correct result) and then ask yourself, "Assuming that I can find the factorial of n−1, can I compute the factorial of n?" In this case, it is clear that you can, by multiplying by n.

递归程序也是如此。当你走到递归调用时,不要顺着执行流,而应当假定这个递归调用能工作(产生正确结果),然后问自己:「假设我能求出 n−1 的阶乘,那我能不能算出 n 的阶乘?」这种情况下答案是显然的:乘以 n 就行。

Of course, it's a bit strange to assume that the function works correctly when you haven't finished writing it, but that's why it's called a leap of faith!

当然,在你还没写完这个函数时就假定它能正确工作,多少有点奇怪——但这正是它被称为「信仰之跃」的原因!

6.7 One more example 6.7 再举一例

After None00144, the most common example of a recursively defined mathematical function is None00145, which has the following definition (see None00146):

None00147 之后,最常见的递归定义数学函数例子就是 None00148,它的定义如下(见 None00149):

fibonacci(0) = 0
fibonacci(1) = 1
fibonacci(n) = fibonacci(n−1) + fibonacci(n−2)

fibonacci(0) = 0
fibonacci(1) = 1
fibonacci(n) = fibonacci(n−1) + fibonacci(n−2)

Translated into Python, it looks like this:

翻译成 Python 是这样的:
None00150

If you try to follow the flow of execution here, even for fairly small values of n, your head explodes. But according to the leap of faith, if you assume that the two recursive calls work correctly, then it is clear that you get the right result by adding them together.

如果你试图顺着这里的执行流走,哪怕 n 只是个很小的值,你的脑袋也会炸掉。但按照信仰之跃,如果你假定那两次递归调用都正确,那么显然把它们加起来就能得到正确结果。

6.8 Checking types 6.8 类型检查

What happens if we call None00151 and give it 1.5 as an argument?

如果我们调用 None00152,并传进去 1.5 作为实参,会发生什么?
None00153

It looks like an infinite recursion. But how can that be? There is a base case—when None00. But if x is not an integer, we can miss the base case and recurse forever.

看起来像是无限递归。但怎么会这样?明明有个基础情形——当 None00 时。可如果 x 不是整数,我们就会错过基础情形,从而永远递归下去。

In the first recursive call, the value of x is 0.5. In the next, it is -0.5. From there, it gets smaller (more negative), but it will never be 0.

在第一次递归调用中,x 的值是 0.5;下一次是 -0.5。此后它会变得更小(更负),却永远不会等于 0。

We have two choices. We can try to generalize the None00160 function to work with floating-point numbers, or we can make None00161 check the type of its argument. The first option is called the gamma function and it's a little beyond the scope of this book. So we'll go for the second.

我们有两种选择。可以尝试把 None00162 函数推广到能处理浮点数,也可以让 None00163 检查实参的类型。第一种选择叫做 gamma 函数,稍稍超出了本书范围。所以我们选第二种。

We can use the built-in function None00164 to verify the type of the argument. While we're at it, we can also make sure the argument is positive:

我们可以用内置函数 None00165 来验证实参的类型。顺手还可以确保实参为正数:
None00166

The first base case handles nonintegers; the second catches negative integers. In both cases, the program prints an error message and returns None to indicate that something went wrong:

两个基础情形:第一个处理非整数,第二个捕获负整数。两种情况下,程序都会打印错误信息并返回 None,表示出了点问题:
None00169

If we get past both checks, then we know that n is positive or zero, so we can prove that the recursion terminates.

如果能通过这两项检查,我们就知道 n 是正数或零,从而可以证明递归会终止。

This program demonstrates a pattern sometimes called a guardian. The first two conditionals act as guardians, protecting the code that follows from values that might cause an error. The guardians make it possible to prove the correctness of the code.

这个程序展示了一种有时被称为卫式条件(guardian pattern)的模式。前两个条件语句充当守卫,把后面的代码保护起来,免遭可能引发错误的值侵扰。有了守卫,才能证明代码的正确性。

In Section 11.3 we will see a more flexible alternative to printing an error message: raising an exception.

在第 11.3 节,我们会看到一种比打印错误信息更灵活的办法:抛出异常。

6.9 Debugging 6.9 调试

Breaking a large program into smaller functions creates natural checkpoints for debugging. If a function is not working, there are three possibilities to consider:

把一个大程序拆成更小的函数,就为调试创造了天然的检查点。如果一个函数不工作,要考虑三种可能:

To rule out the first possibility, you can add a None0 statement at the beginning of the function and display the values of the parameters (and maybe their types). Or you can write code that checks the preconditions explicitly.

要排除第一种可能,你可以在函数开头加一条 None0 语句,显示各个形参的值(以及可能的类型)。或者你也可以写代码显式地检查前置条件。

If the parameters look good, add a None0 statement before each None00 statement that displays the return value. If possible, check the result by hand. Consider calling the function with values that make it easy to check the result (as in Section 6.2).

如果形参看起来没问题,就在每条 None00 语句之前加一条 None0 语句,把返回值显示出来。如果可能,动手验算一下结果。考虑用那些便于核验结果的值来调用函数(如第 6.2 节所示)。

If the function seems to be working, look at the function call to make sure the return value is being used correctly (or used at all!).

如果函数看起来能工作,就去查看函数调用,确认返回值被正确地使用了(或者至少被用到了!)。

Adding print statements at the beginning and end of a function can help make the flow of execution more visible. For example, here is a version of None00176 with print statements:

在函数的开头和结尾加 print 语句,有助于让执行流更清晰可见。例如,下面这个版本的 None00177 就带 print 语句:
None00178

None0 is a string of space characters that controls the indentation of the output. Here is the result of None00180:

None0 是一串空格字符,用来控制输出的缩进。下面是 None00182 的运行结果:
None00183

If you are confused about the flow of execution, this kind of output can be helpful. It takes some time to develop effective scaffolding, but a little bit of scaffolding can save a lot of debugging.

如果你对执行流感到困惑,这类输出会很有帮助。写出有效的脚手架需要花些时间,但一点点脚手架就能省下大量调试功夫。

6.10 Glossary 6.10 术语表

temporary variable:
A variable used to store an intermediate value in a complex calculation.
dead code:
Part of a program that can never be executed, often because it appears after a None00 statement.
None:
A special value returned by functions that have no return statement or a return statement without an argument.
incremental development:
A program development plan intended to avoid debugging by adding and testing only a small amount of code at a time.
scaffolding:
Code that is used during program development but is not part of the final version.
guardian:
A programming pattern that uses a conditional statement to check for and handle circumstances that might cause an error.
临时变量:
在复杂计算中用来保存中间值的变量。
死代码:
程序中永远无法被执行到的部分,通常因为它出现在 None00 语句之后。
None
由那些没有 return 语句、或 return 语句不带实参的函数返回的特殊值。
增量开发:
一种程序开发方案,旨在通过每次只添加并测试少量代码来避免调试。
脚手架:
在程序开发过程中使用、却不属于最终版本的代码。
卫式条件(guardian pattern):
一种编程模式,用条件语句检查并处理可能引发错误的情形。

6.11 Exercises 6.11 习题

Exercise 4

习题 4

Draw a stack diagram for the following program. What does the program print? Solution: None00188.

为下面的程序画一个栈图。这个程序会打印什么?答案:None00189。
None00190

Exercise 5

习题 5

The Ackermann function, A(m, n), is defined:
A(m, n) =
        n+1              if m = 0
        A(m−1, 1)         if m > 0 and n = 0
        A(m−1, A(m, n−1))  if m > 0 and n > 0.
See None00191. Write a function named x00 that evaluates Ackermann's function. Use your function to evaluate None00193, which should be 125. What happens for larger values of x and x? Solution: None00196.

Ackermann 函数 A(m, n) 定义如下:
A(m, n) =
        n+1              当 m = 0
        A(m−1, 1)         当 m > 0 且 n = 0
        A(m−1, A(m, n−1))  当 m > 0 且 n > 0。
None00197。写一个名为 x00 的函数来求值 Ackermann 函数。用你的函数求值 None00199,结果应为 125。当 xx 更大时会发生什么?答案:None00202。

Exercise 6

习题 6

A palindrome is a word that is spelled the same backward and forward, like "noon" and "redivider". Recursively, a word is a palindrome if the first and last letters are the same and the middle is a palindrome.

回文词是指正着拼和反着拼一样的单词,比如 "noon" 和 "redivider"。递归地说,如果一个词的首尾字母相同、且中间部分也是回文,那它就是回文。

The following are functions that take a string argument and return the first, last, and middle letters:

下面是几个接收字符串实参、分别返回首字母、尾字母和中间部分的字母的函数:
None00203

We'll see how they work in Chapter 8.

我们将在第 8 章看到它们如何工作。
  1. Type these functions into a file named None00204 and test them out. What happens if you call None00 with a string with two letters? One letter? What about the empty string, which is written x0 and contains no letters?
  2. Write a function called None00207 that takes a string argument and returns None if it is a palindrome and None0 otherwise. Remember that you can use the built-in function x00 to check the length of a string.
  1. 把这些函数敲进一个名为 None00211 的文件并测试。如果你用含有两个字母的字符串调用 None00 会怎样?一个字母呢?空字符串(写作 x0,不含任何字母)又如何?
  2. 写一个名为 None00214 的函数,接收字符串实参,若是回文则返回 None,否则返回 None0。记住你可以用内置函数 x00 检查字符串长度。

Solution: None00218.

答案:None00219。

Exercise 7

习题 7

A number, a, is a power of b if it is divisible by b and a/b is a power of b. Write a function called None0022 that takes parameters x and x and returns None if x is a power of x. Note: you will have to think about the base case.

一个数 a 是 b 的幂,当且仅当它能被 b 整除、且 a/b 也是 b 的幂。写一个名为 None0022 的函数,接收实参 xx,若 a 是 b 的幂则返回 None。注意:你得想清楚基础情形。

Exercise 8

习题 8

The greatest common divisor (GCD) of a and b is the largest number that divides both of them with no remainder.

a 和 b 的最大公约数(GCD)是能同时整除二者且没有余数的最大整数。

One way to find the GCD of two numbers is based on the observation that if r is the remainder when a is divided by b, then gcd(a, b) = gcd(b, r). As a base case, we can use gcd(a, 0) = a.

求两个数最大公约数的一种方法基于这样的观察:若 r 是 a 除以 b 的余数,则 gcd(a, b) = gcd(b, r)。作为基础情形,可以用 gcd(a, 0) = a。

Write a function called x00 that takes parameters x and x and returns their greatest common divisor.

写一个名为 x00 的函数,接收实参 xx,返回它们的最大公约数。

Credit: This exercise is based on an example from Abelson and Sussman's Structure and Interpretation of Computer Programs.

说明:本题改编自 Abelson 与 Sussman 的《计算机程序的构造和解释》。