@@ -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