仅用作个人学习记录
Reference:https://inst.eecs.berkeley.edu/~cs61a
http://composingprograms.com/pages/33-exceptions.html
汉诺塔问题
起初,所有的圆盘都在第一柱,目标是将所有的圆盘移到第三柱。
每次只能移动最上面的圆盘,圆盘必须移到空闲柱或当前顶部圆盘大于移动盘的柱子。
移动策略
- 当只有一个圆盘需要移动,直接移到第三柱即可。
- 当有 N 个圆盘(N > 1)需要移动:
- 先将上面的 N - 1 个圆盘移到空闲柱;
- 再将底部最大的圆盘移到第三柱;
- 最后将剩余的 N - 1 个圆盘移到第三柱。
上述策略几乎可以直接转化为递归函数。
def move_tower(n, start_peg, end_peg):
"""Perform moves that transfer an ordered tower of N>0 disks in the
Towers of Hanoi puzzle from peg START PEG to peg END PEG, where
1 <= START PEG, END PEG <= 3, and START PEG != END PEG. Assumes
the disks to be moved are all smaller than those on the other pegs."""
if n == 1:
move_disk(start_peg, end_peg)
else:
spare_peg = 6 - start_peg - end_peg
move_tower(n - 1, start_peg, spare_peg)
move_disk(start_peg, end_peg)
move_tower(n - 1, spare_peg, end_peg)
异常
异常是编程语言中内置的一种机制,用来定义和解决一些异常的情况。
例如,提供给函数的参数不符合先决条件,如给move_tower
提供的 n 小于 0,这是我们不希望看到的。
对异常的操作包括终止计算的控制语句,并允许程序员采取纠正措施。
raise
为了表示一个异常,程序会抛出一个异常(raises an exception),在 Python 中,这意味着创建一个异常值并对其应用 raise 语句。
raise <expression>
if N <= 0:
raise ValueError("Number of disks must be positive")
raise 之后的表达式创建了一种异常值(ValueError 类型通常用于指示不正确的值。)
>>> 1 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> raise ValueError('sss')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: sss
>>> assert False
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
>>> raise AssertionError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
raise 与 assert: assert 在没有给出真值时,会执行 raise。assert 就像 raise 的外包装。用 assert 检查内部的编程逻辑,用异常处理输入。
try
>>> def f();
File "<stdin>", line 1
def f();
^
SyntaxError: invalid syntax
>>> def f():
... g()
... print('after call g')
>>> def g():
... raise ValueError
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
File "<stdin>", line 2, in g
ValueError
注意到,进入 g 函数后程序抛出了一个异常,g 结束返回 f 导致 f 也直接终止,print 语句没有执行。无论异常出现在哪里,它会不断向上传递直到顶部。
但是,我们大多情况下不希望这样。当预计可能会发生异常并且有比终止程序更有用的响应(告知用户、处理异常)时,可以使用 try 语句捕获引发的异常。
try:
<try suite>
except <exception class> as <name>:
<except suite>
...
<try suite> 始终在执行 try 语句时立即执行。 except 子句组仅在执行 <try suite> 过程中出现异常时执行。每个 except 子句指定要处理的特定异常类。例如,如果 <exception class> 是 AssertionError,那么在执行 <try suite> 过程中引发的任何继承自 AssertionError 的类的实例都将由以下 <except suite> 处理。在 <except suite> 中,标识符 <name> 绑定到引发的异常对象,但此绑定不会在 <except suite> 之外持续存在。except 后要是没有异常类,则捕获所有类型的异常。
例如,我们可以使用 try 语句处理 ZeroDivisionError 异常,该语句在引发异常时将名称 x 绑定到 0。
>>> try:
x = 1/0
except ZeroDivisionError as e:
print('handling a', type(e))
x = 0
handling a <class 'ZeroDivisionError'>
>>> x
0
当异常产生时,控制流会直接跳到最近的try
语句的能够处理该异常类型的<except suite>
的主体中。一旦异常被捕获了,就不会在向上传递了。
>>> def invert(x):
result = 1/x # Raises a ZeroDivisionError if x is 0
print('Never printed if x is 0')
print(result)
>>> def invert_safe(x):
try:
invert(x)
except ZeroDivisionError as e:
print(e)
>>> invert_safe(2)
Never printed if x is 0
0.5
>>> invert_safe(0)
division by zero
>>> try:
invert_safe(0)
except ZeroDivisionError:
print('more exteral exception')
division by zero
Exercise: Removing Digits
问题:定义一个函数,从给定数字中删除特定数字 (0–9),并返回。
def remove_digit(n, digit):
"""Assuming N>=0, 0 <= DIGIT <= 9, return a number whose
base-10 representation is the same as N, but with all instances of
DIGIT removed. If all digits removed, return 0.
>>> remove_digit(123, 3)
12
>>> remove_digit(1234, 5)
1234
>>> remove_digit(1234, 1)
234
>>> remove_digit(111111, 1)
0
"""
if n == 0:
return 0
if n % 10 == digit:
return remove_digit(n // 10, digit)
return n % 10 + remove_digit(n // 10, digit) * 10