Skip to content

Commit 04a0ce7

Browse files
author
wolf
committed
chapter05
1 parent cac76cb commit 04a0ce7

File tree

1 file changed

+371
-3
lines changed

1 file changed

+371
-3
lines changed

thinkpython/tex-zh/part/chapter05.tex

Lines changed: 371 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -331,14 +331,14 @@ \section{递归}
331331
\end{quote}
332332

333333
接受{\tt n=1}的{\tt countdown}返回。
334-
\edn{quote}
335-
336-
接受{\tt n=2}的{\tt countdown}返回。
337334
\end{quote}
338335

339336
接受{\tt n=2}的{\tt countdown}返回。
340337
\end{quote}
341338

339+
%接受{\tt n=3}的{\tt countdown}返回。
340+
%\end{quote}
341+
342342
{\tt countdown}接受{\tt n=3}的函数返回。
343343

344344
然后,我们就会到\verb"__main__"里了。整个输出如下:
@@ -386,6 +386,374 @@ \section{递归}
386386
\index{stack diagram 堆栈图}
387387
\index{function frame 函数图}
388388
\index{frame 图}
389+
390+
\ref{堆栈图}部分,我们使用堆栈图代表程序在函数调用过程中的状态。
391+
同样的方法也可以帮助我们理解递归函数。\\
392+
393+
每次函数调用的时候,Python创建一个新的函数框图,里面包含了函数的局部
394+
变量和参数。对于递归函数来说,可能会有不止一个框图同时出现在堆栈图里
395+
\\
396+
397+
下图显示了{\tt n = 3}时{\tt countdown}函数的堆栈图。
398+
399+
\beforefig
400+
\centerline{\includegraphics{figs/stack2.eps}}
401+
\afterfig
402+
403+
像通常的一样,栈顶是\verb"__main__"的卡框图。由于我们没有在\verb"__main__"里创建任何的变量或传递任何的参数给它,所以它是空的。
404+
405+
\index{base case 终止条件}
406+
\index{recursion!base case}
407+
408+
四个{\tt countdown}框图拥有不同的{\tt n}值。栈底,{\tt n = 0},叫做
409+
终止条件(base case)。不执行任何的递归调用,所以就没有更多的框图了。
410+
411+
\begin{quote}
412+
\verb"print_n"的堆栈图,其中,\verb"s = 'Hello'" {\tt n = 2}。
413+
\end{quote}
414+
415+
\begin{quote}
416+
编写\verb"do_n"函数,接受一个函数对象,和一个数字,{\tt n}作为参数,调用传递过来的函数{\tt n}次。
417+
\end{quote}
418+
419+
\section{无穷递归}
420+
\index{infinite recursion 无穷递归}
421+
\index{recursion!infinite}
422+
\index{runtime error 运行时错误}
423+
\index{error!runtime}
424+
\index{traceback 跟踪}
425+
426+
如果递归函数没有终止条件,就会无止境的递归\footnote{译者:直到消耗完资源,或者操作系统终止它}。当达到最大递归深度时,Python就会报告错误
427+
信息。
428+
429+
\index{exception!RuntimeError}
430+
\index{RuntimeError}
431+
432+
\beforeverb
433+
\begin{verbatim}
434+
File "<stdin>", line 2, in recurse
435+
File "<stdin>", line 2, in recurse
436+
File "<stdin>", line 2, in recurse
437+
.
438+
.
439+
.
440+
File "<stdin>", line 2, in recurse
441+
RuntimeError: Maximum recursion depth exceeded
442+
\end{verbatim}
443+
\afterverb
444+
445+
这个追踪比我们上一章看到的要大很多。当错误产生时,在栈中有1000张{\tt recurse}框图!
446+
447+
\section{键盘输如}
448+
\index{keyboard input 键盘输入}
449+
450+
迄今为止,我们编写的程序对用户来说有点“不礼貌”---不接受来自用户的输入。每次只是做同样的事。\\
451+
452+
Python提供了一个内置的函数\verb"raw_input"获取用户的输入\footnote{在
453+
Python3.0中,这个函数叫做{\tt input}。 译注:在Python3.x中都是如此}
454+
\verb"raw_input"函数被调用时,程序停下来等待用户输入些东西。当用户敲击{\sf Return}或者{\sf Enter},程序恢复运行,\verb"raw_input"把用户输入的东西作为字符串返回。
455+
456+
\index{Python 3.0}
457+
\index{raw\_input function raw\_input函数}
458+
\index{function!raw\_input}
459+
460+
461+
\beforeverb
462+
\begin{verbatim}
463+
>>> input = raw_input()
464+
What are you waiting for?
465+
>>> print input
466+
What are you waiting for?
467+
\end{verbatim}
468+
\afterverb
469+
470+
在用户输入之前,最好能够输出一个提示,告诉用户输入什么。\verb"raw_input"可以接受一个提示作为参数。
471+
472+
\index{prompt 提示}
473+
474+
\beforeverb
475+
\begin{verbatim}
476+
>>> name = raw_input('What...is your name?\n')
477+
What...is your name?
478+
Arthur, King of the Britons!
479+
>>> print name
480+
Arthur, King of the Britons!
481+
\end{verbatim}
482+
\afterverb
483+
484+
提示后面的\verb"n"代表一个换行,这是一个特殊字符,产生一个断行。
485+
这也是为什么用户的输入出现在提示下面的原因。
486+
487+
\index{newline 换行}
488+
489+
如果希望用户输入一个整数,可以尝试把返回值转换为{\tt int}。
490+
491+
\beforeverb
492+
\begin{verbatim}
493+
>>> prompt = 'What...is the airspeed velocity of an unladen swallow?\n'
494+
>>> speed = raw_input(prompt)
495+
What...is the airspeed velocity of an unladen swallow?
496+
17
497+
>>> int(speed)
498+
17
499+
\end{verbatim}
500+
\afterverb
501+
502+
但是,如果用户输入的不是数字组成的字符串,就会得到一个错误:
503+
504+
\beforeverb
505+
\begin{verbatim}
506+
>>> speed = raw_input(prompt)
507+
What...is the airspeed velocity of an unladen swallow?
508+
What do you mean, an African or a European swallow?
509+
>>> int(speed)
510+
ValueError: invalid literal for int()
511+
\end{verbatim}
512+
\afterverb
513+
514+
我们以后将会看到如何处理这样的错误。
515+
516+
\index{ValueError }
517+
\index{exception!ValueError}
518+
519+
520+
\section{Debugging 调试}
521+
\label{whitespace}
522+
\index{debugging 调试}
523+
\index{traceback 跟踪}
524+
525+
当错误发生,Python 输出的跟踪信息,包含大量的信息,但是也很容易让人
526+
“眼花缭乱”,特别是栈中有很多框图的时候。最有用的部分通常是:
527+
528+
\begin{itemize}
529+
530+
\item 什么类型的错误
531+
532+
\item 在什么地方发生的
533+
534+
\end{itemize}
535+
536+
通常,语法错误很容易发现,但是也有些微妙的东西\footnote{原文是:but
537+
there are a few gotchas}。“空格”错误可能很微妙,因为空格和制表是不可见的,而且我们习惯上忽略它们\footnote{特别是在不同的机器上,或者不同
538+
的工具上大开源码的时候}。
539+
540+
\index{whitespace 空格}
541+
542+
543+
\beforeverb
544+
\begin{verbatim}
545+
>>> x = 5
546+
>>> y = 6
547+
File "<stdin>", line 1
548+
y = 6
549+
^
550+
SyntaxError: invalid syntax
551+
\end{verbatim}
552+
\afterverb
553+
554+
这个例子中,问题发生在第二行有了一个空格缩进。但是错误指向了{\tt y},
555+
这个是一个误导。一般,错误信息提示问题发生的地方,当时实际的错误可能
556+
在提示的前面一点,有时在前一行。
557+
558+
\index{error!runtime}
559+
\index{runtime error 运行时错误}
560+
561+
对于运行时错误,也是如此。假设,你想用分贝计算信噪比。公式是$SNR_{db} = 10 \log_{10} (P_{signal} / P_{noise})$。你可能写出如下的Python代码:
562+
563+
\beforeverb
564+
\begin{verbatim}
565+
import math
566+
signal_power = 9
567+
noise_power = 10
568+
ratio = signal_power / noise_power
569+
decibels = 10 * math.log10(ratio)
570+
print decibels
571+
\end{verbatim}
572+
\afterverb
573+
但是,当你运行时,你会得到一个错误信息\footnote{在Python3.0中,不会
574+
得到错误信息;除法运算符执行浮点除,尽管操作数是整数,这个和实际
575+
很贴近}。
576+
577+
\index{exception!OverflowError}
578+
\index{OverflowError}
579+
580+
\beforeverb
581+
\begin{verbatim}
582+
Traceback (most recent call last):
583+
File "snr.py", line 5, in ?
584+
decibels = 10 * math.log10(ratio)
585+
OverflowError: math range error
586+
\end{verbatim}
587+
\afterverb
588+
589+
错误信息提示错误发生在第五行,但是那行根本没有错误。为了发掘出真正的错误,打印{\tt ratio}的值可能会有帮助,结果显示是0。问题出现在第四行,因为两个整数的除法实施的是地板除(floor division)\footnote{译注:
590+
Python核心编程的中译本中,把它翻译成地板除}。解决方案是用浮点数来表示
591+
信号功率和噪声功率。
592+
593+
\index{floor division 地板除}
594+
\index{division!floor}
595+
596+
总的来书,错误信息告诉我们出现问题的地方,但通常不是问题发生的根本所在。
597+
598+
\section{术语表}
599+
600+
\begin{description}
601+
602+
\item [modulus operator 模运算符:] 一个操作符,记法为百分号({\tt \%})。作用在两个整数之间,产生余数。
603+
\index{modulus operator 模运算符}
604+
\index{operator!modulus}
605+
606+
\item [boolean expression 布尔表达式:]值要么为{\tt True}要么为{\tt False}的表达式。
607+
\index{boolean expression 布尔表达式}
608+
\index{expression!boolean}
609+
610+
\item [relational operator 关系运算符:] 比较操作数的的运算符:
611+
{\tt ==},{\\t !=},{\tt >},{\tt <},{\tt >=}和{\tt <=}。
612+
613+
\item [logical operator 逻辑运算符:]连接布尔表达式的运算符:
614+
{\tt and},{\tt or}和{\tt not}。
615+
616+
\item [conditional statement 条件语句:]依靠一些条件控制执行流的语句。
617+
\index{conditional statement 条件语句}
618+
\index{statement!conditional}
619+
620+
\item [condition 条件:] 条件语句中的布尔表达式,决定分支的执行。
621+
\index{condition 条件}
622+
623+
\item [compound statement 复合语句:]包含头和体的语句。头一(:)结尾,体依据头,缩进。
624+
\index{compound statement}
625+
626+
\item [body 体:] 符合语句中的一系列语句。
627+
\index{body}
628+
629+
\item [branch 分支:]条件语句中的可选择执行的语句(序列)。
630+
\index{branch}
631+
632+
\item [chained conditional 链式条件语句:] 拥有一系列的选择分之的条件语句。
633+
\index{chained conditional 链式条件语句}
634+
\index{conditional!chained}
635+
636+
\item [nested conditional 嵌套条件语句:]出现在条件语句中分分支中的条件语句。
637+
\idex{nested conditional 嵌套条件语句}
638+
\index{conditional!nested}
639+
640+
\item[recursion 递归:]调用函数自身的过程。
641+
\index{recursion 递归}
642+
643+
\item[base case 终止条件:] 递归函数里的一个条件分支,终止递归调用。
644+
\index{base case}
645+
646+
\item[infinite recursion 无穷递归:]没有终止条件的递归,最终无穷递归产生一个运行时错误。
647+
\index{infinite recursion 无穷递归}
648+
649+
\end{description}
650+
651+
\section{练习}
652+
653+
\begin{费马最后定理}
654+
655+
656+
费马最后定理这么表述:不存在这样的整数$a$,$b$$c$使得对于$n$大于2,\[ a^n + b^n = c^n \]
657+
\begin{enumerate}
658+
659+
\item 编写\verb"check_fermat"函数,接受4个参数---{\tt a},{\tt b},{\tt c}和{\tt n}---验证费马定理是否正确。如果$n$大于2,\[a^n + b^n = c^n \]是成立的,程序输出,``Holy smokes, Fermat was wrong!'',否则,输出,``No, that doesn't work.''
660+
661+
\item 编写一个函数提示用户输入 {\tt a}, {\tt b}, {\tt c} and {\tt n}的值,把他们转换成整数,使用\verb"check_fermat"验证是否违背费马定理。
662+
663+
\end{enumerate}
664+
\end{ex}
665+
666+
667+
\begin{ex}
668+
\index{triangle 三角形}
669+
670+
给你三根木棒,你也许能,也许不能组成一个三角形。比如,其中一根木棒12英尺长,其他的两根是1英尺长,很明显,不能使短木棒在长木棒的中间相遇。
671+
对于三个任意长度,可以用一个简单的测试在检测是否能够构成一个三角形。
672+
673+
\begin{quotation}
674+
“如果三个长度的任意一个大于其他两个之和,就不能构成一个三角形。否则,就可以\footnote{如果两个长度之和等于第三个,他们形成退化的三角形。}“
675+
\end{quotation}
676+
677+
678+
\begin{enumerate}
679+
680+
\item 编写\verb"is_triangle"函数,接受3个整数作为参数, 依据能不能构成三角形,输出要么是“Yes“,要么是“or“。
681+
682+
\item 编写一个函数提示用户输入木棒的长度,转换成整数,然后调用\verb"is_triangle"检测是否可以构成一个三角形。
683+
684+
\end{enumerate}
685+
686+
\end{ex}
687+
688+
接下来的练习使用\ref{turtlechap}章节的TurtleWorld。
689+
690+
\index{TurtleWorld}
691+
\begin{ex}
692+
693+
阅读下面的函数,看看它的功能是什么。然后运行它(查看\ref{turtlechap}章节的例子)。
694+
695+
\beforeverb
696+
\begin{verbatim}
697+
def draw(t, length, n):
698+
if n == 0:
699+
return
700+
angle = 50
701+
fd(t, length*n)
702+
lt(t, angle)
703+
draw(t, length, n-1)
704+
rt(t, 2*angle)
705+
draw(t, length, n-1)
706+
lt(t, angle)
707+
bk(t, length*n)
708+
\end{verbatim}
709+
\afterverb
710+
711+
\end{ex}
712+
713+
\begin{ex}
714+
\index{koch curve 柯霍曲线}
715+
716+
柯霍曲线是一个分形体,看起来像这样:
717+
718+
\beforefig
719+
\centerline{\includegraphics[height=1in]{figs/koch.eps}}
720+
\afterfig
721+
722+
画长度为$x$的柯霍曲线,你所需要做得就是
723+
\begin{enumerate}
724+
725+
\item 画一个长度为$x/3$的柯霍曲线
726+
727+
\item 左转60度。
728+
729+
\item 画一个长度为$x/3$的柯霍曲线
730+
731+
\item 右转120度
732+
733+
\item 画一个长度为$x/3$的柯霍曲线
734+
735+
\item 左转60度。
736+
737+
\item 画一个长度为$x/3$的柯霍曲线
738+
739+
\end{enumerate}
740+
741+
唯一的例外是如果$x$小于3,此时,就直接画一个长度为$x$的直线。
742+
743+
\begin{enumerate}
744+
745+
\item 编写{\tt koch}函数,接受一个turtle和长度作为参数,使用turtle画
746+
一个给定长度的柯霍曲线。
747+
748+
\item 编写{\tt snowflake}函数,画3个柯霍曲线,形成雪花的轮廓。
749+
750+
可以查看我的答案\url{thinkpython.com/code/koch.py}。
751+
752+
\item 柯霍曲线可以用多种方式一般化。查看\url{wikipedia.org/wiki/Koch_snowflake}例子,并实现自己喜欢的雪花。
753+
754+
\end{enumerate}
755+
\end{ex}
756+
389757

390758

391759

0 commit comments

Comments
 (0)