第一章
在第一章中,我们专注于计算过程,以及程序设计中函数的作用。我们看到了如何使用原始
数据(数值)和原始操作(算术运算),如何通过组合和控制来形成复合函数,以及如何通
过给予过程名称来创建函数抽象。我们也看到了高阶函数通过操作通用计算方法来提升语言
的威力。这是编程的本质。
牛顿法求根
平方根
def square_root(a):
return find_root(lambda x: square(x) - a)
对数
def logarithm(a, base=2):
return find_root(lambda x: pow(base, x) - a)
迭代改进算法
def newton_update(f):
def update(x):
return x - f(x) / approx_derivative(f, x)
return update
函数的导数(斜率)
def approx_derivate(f, x, delta = 1e-5):
df = f(x + delta) - f(x)
return df/delta
main:
def find_root(f, initial_guess = 10):
f(x) = square(x) - 16
def test(x):
return approx_eq(f(x), 0)
return iter_improve(newton_update(f), test, initial_guess)
iter_improve
def iter_improve(newton_update(f), test, guess = initial_guess):
while not test(guess): #当 guess 不为0
guess = newton_update(guess)
return guess
测试相似性, 若相似返回 1
def approx_eq(x, y, tolerance = 1e-5):
return abs(x - y) < tolerance
1.6.8 函数装饰器
def trace1(fn):
def wrapped(x):
print('->', fb, '(', x, ')')
return fn(x)
return wrapped
>>> trace1
def triple(x):
return 3 * x
>>> triple(12)
-> <function triple at 0x102a39848> ( 12 )
36
像通常一样,函数 triple 被创建了,但是, triple 的名称并没有绑定到这个函数上,
而是绑定到了在新定义的函数 triple 上调用 trace1 的返回函数值上。在代码中,这
个装饰器等价于:
>>> def triple(x):
return 3 * x
>>> triple = trace1(triple)
第二章 使用对象构建抽象
(专注于数据)
2.2数据抽象
由于我们希望在程序中表达世界中的大量事物,我们发现它们的大多数都具有复合结构。日
期是年月日,地理位置是精度和纬度。为了表示位置,我们希望程序语言具有将精度和纬
度“粘合”为一对数据的能力 -- 也就是一个复合数据结构 -- 使我们的程序能够以一种方式操作
数据,将位置看做单个概念单元,它拥有两个部分。
复合数据的使用也让我们增加程序的模块性。如果我们可以直接将地理位置看做对象来操
作,我们就可以将程序的各个部分分离,它们根据这些值如何表示来从本质上处理这些值。
将某个部分从程序中分离的一般技巧是一种叫做数据抽象的强大的设计方法论。这个部分用
于处理数据表示,而程序用于操作数据。数据抽象使程序更易于设计、维护和修改。
数据抽象的特征类似于函数抽象。当我们创建函数抽象时,函数如何实现的细节被隐藏了,
而且特定的函数本身可以被任何具有相同行为的函数替换。换句话说,我们可以构造抽象来
使函数的使用方式和函数的实现细节分离。与之相似,数据抽象是一种方法论,使我们将复
合数据对象的使用细节与它的构造方式隔离。
数据抽象的基本概念是构造操作抽象数据的程序。也就是说,我们的程序应该以一种方式来
使用数据,对数据做出尽可能少的假设。同时,需要定义具体的数据表示,独立于使用数据
的程序。我们系统中这两部分的接口是一系列函数,叫做选择器和构造器,它们基于具体表
示实现了抽象数据。