# SICP 第一章 使用函数抽象概念 1.6 高阶函数

## 1.6 高阶函数

``````>>> def square(x):
return x * x
``````

``````>>> 3 * 3
9
>>> 5 * 5
25
``````

### 1.6.1 作为参数的函数

``````>>> def sum_naturals(n):
total, k = 0, 1
while k <= n:
total, k = total + k, k + 1
>>> sum_naturals(100)
5050
``````

``````>>> def sum_cubes(n):
total, k = 0, 1
while k <= n:
total, k = total + k*k*k, k + 1
>>> sum_cubes(100)
25502500
``````

``````>>> def pi_sum(n):
total, k = 0, 1
while k <= n:
total, k = total + 8 / ((4*k-3) * (4*k-1)), k + 1
>>> pi_sum(100)
3.1365926848388144
``````

``````def <name>(n):
total, k = 0, 1
while k <= n:
total, k = total + <term>(k), k + 1
``````

``````使用`identity`函数来返回其参数，我们还可以使用完全相同的`summation`求和函数来计算自然数之和。
>>> def summation(n, term):
total, k = 0, 1
while k <= n:
total, k = total + term(k), k + 1
>>> def identity(x):
return x
>>> def sum_naturals(n):
return summation(n, identity)
>>> sum_naturals(10)
55
``````

`summation`求和函数也可以直接调用，而不需要为特定序列定义另一个函数。

``````>>> summation(10, square)
385
``````

``````>>> def pi_term(x):
return 8 / ((4*x-3) * (4*x-1))
>>> def pi_sum(n):
return summation(n, pi_term)
>>> pi_sum(1e6)
3.141592153589902
``````

### 1.6.2 作为一般方法的函数

``````>>> def improve(update, close, guess=1):
while not close(guess):
guess = update(guess)
return guess
``````

`improve`改进函数是重复细化的通用表达。 它不会具体指定要解决的问题：这些细节都留给`update`更新函数和`close`关闭函数来解决。

``````>>> def golden_update(guess):
return 1/guess + 1
>>> def square_close_to_successor(guess):
return approx_eq(guess * guess, guess + 1)
``````

``````>>> def approx_eq(x, y, tolerance=1e-15):
return abs(x - y) < tolerance
``````

``````>>> improve(golden_update, square_close_to_successor)
1.6180339887498951
``````

``````>>> from math import sqrt
>>> phi = 1/2 + sqrt(5)/2
>>> def improve_test():
approx_phi = improve(golden_update, square_close_to_successor)
assert approx_eq(phi, approx_phi), 'phi differs from its approximation'
>>> improve_test()
``````

### 1.6.3 定义函数 III:嵌套定义

``````>>> def average(x, y):
return (x + y)/2
>>> def sqrt_update(x, a):
return average(x, a/x)
``````

``````>>> def sqrt(a):
def sqrt_update(x):
return average(x, a/x)
def sqrt_close(x):
return approx_eq(x * x, a)
return improve(sqrt_update, sqrt_close)
``````

1.每个用户定义的函数都有父环境：它的定义所在的环境。
2.当用户定义的函数被调用时，其局部帧扩展于其父环境。
`sqrt`之前，所有函数都是在全局环境中定义的，所以它们都关联到全局环境。相比之下，当Python运行`sqrt`的前两个子句时，它创建了与本地环境相关联的函数。在

``````>>> sqrt(256)
16.0
``````

`sqrt_update`函数体中的返回表达式可以通过跟随这一系列帧来解析值。我们可以通过查找在当前环境的名称找到绑定到该名称的第一个值。 Python首先在`sqrt_update`帧中检查 -- 不存在。接下来，Python检查父帧中`f1`，并找到`a`绑定到256。

1. 局部函数的名称不会影响到定义函数外部的名称，因为局部函数名称将绑定到了定义处的当前局部环境中,而不是全局环境中。
2. 局部函数可以访问外层函数的环境，这是因为局部函数的函数体的求值环境扩展于定义处的求值环境。

`sqrt_update`函数自带有一些数据：在定义它的环境中引用的值。因为它以这种方式“封装”信息，所以局部定义的函数通常被称为闭包

### 1.6.4 作为返回值的函数

``````>>> def compose1(f, g):
def h(x):
return f(g(x))
return h
``````

`compose1`中的`1`意味着复合函数只采用单个参数。 该命名惯例不由解释器强制执行; `1`只是函数名称的一部分。

### 1.6.5 例子:牛顿法

``````>>> def newton_update(f, df):
def update(x):
return x - f(x) / df(x)
return update
``````

``````>>> def find_zero(f, df):
def near_zero(x):
return approx_eq(f(x), 0)
return improve(newton_update(f, df), near_zero)
``````

1. 64的平方根为8，因为8·8 = 64。
2. 64的立方根为4，因为4·4·4 = 64。
3. 64的六次方根是2，因为2⋅2⋅2⋅2⋅2⋅2= 64。

``````>>> def square_root_newton(a):
def f(x):
return x * x- a
def df(x):
return 2 * x
return find_zero(f, df)
>>> square_root_newton(64)
8.0
``````

``````>>> def power(x, n):
"""Return x * x * x * ... * x for x repeated n times."""
product, k = 1, 0
while k < n:
product, k = product * x, k + 1
return product
>>> def nth_root_of_a(n, a):
def f(x):
return power(x, n) - a
def df(x):
return n * power(x, n-1)
return find_zero(f, df)
>>> nth_root_of_a(2, 64)
8.0
>>> nth_root_of_a(3, 64)
4.0
>>> nth_root_of_a(6, 64)
2.0
``````

### 1.6.6 函数柯里化

``````>>> def curried_pow(x):
def h(y):
return pow(x, y)
return h
>>> curried_pow(2)(3)
8
``````

``````>>> def map_to_range(start, end, f):
while start < end:
print(f(start))
start = start + 1
``````

``````>>> map_to_range(0, 10, curried_pow(2))
1
2
4
8
16
32
64
128
256
512
``````

``````>>> def curry2(f):
"""Return a curried version of the given two-argument function."""
def g(x):
def h(y):
return f(x, y)
return h
return g
>>> def uncurry2(g):
"""Return a two-argument version of the given curried function."""
def f(x, y):
return g(x)(y)
return f
>>> pow_curried = curry2(pow)
>>> pow_curried(2)(5)
32
>>> map_to_range(0, 10, pow_curried(2))
1
2
4
8
16
32
64
128
256
512
``````

`curry2`函数使用了双参数函数`f`，并返回单参数函数`g`。 当`g`接受参数`x`时，它返回一个单参数函数`h`。 当`h`接受参数`y`时，它调用了`f(x，y)`。 因此，`curry2(f)(x)(y)`等价于`f(x，y)``uncurry2`函数反转`currying`变换，使得`uncurry2(curry2(f))`等效于`f`

``````>>> uncurry2(pow_curried)(2, 5)
32
``````

### 1.6.7 Lambda 表达式

``````>>> def compose1(f, g):
return lambda x: f(g(x))
``````

lambda表达式的结果称为lambda函数。 它没有内在名称（因此Python为该名称打印<lambda>），但它的行为就像任何其他函数一样。

``````>>> s = lambda x: x * x
>>> s
<function <lambda> at 0xf3f490>
>>> s(12)
144
``````

``````>>> compose1 = lambda f,g: lambda x: f(g(x))
``````

It may seem perverse to use lambda to introduce a procedure/function. The notation goes back to Alonzo Church, who in the 1930's started with a "hat" symbol; he wrote the square function as "ŷ . y × y". But frustrated typographers moved the hat to the left of the parameter and changed it to a capital lambda: "Λy . y × y"; from there the capital lambda was changed to lowercase, and now we see "λy . y × y" in math books and (lambda (y) (* y y)) in Lisp.

``````                                     —Peter Norvig (norvig.com/lispy2.html)
``````

### 1.6.8 抽象和一等函数

1. 它们可以被绑定到名字。
2. 它们可以作为参数传递给函数。
3. 它们可以作为函数的返回值返回。
4. 它们可以被包括在数据结构中。

Python 总是给予函数一等地位, 所产生的表现力的收益是巨大的。

### 1.6.9 函数装饰器

Python提供了特殊的语法, 将高阶函数用作执行`def`语句的一部分,叫做装饰器。 也许最常见的例子是`trace`跟踪。

``````>>> def trace(fn):
def wrapped(x):
print('-> ', fn, '(', x, ')')
return fn(x)
return wrapped
>>> @trace
def triple(x):
return 3 * x
>>> triple(12)
->  <function triple at 0x102a39848> ( 12 )
36
``````

``````>>> def triple(x):
return 3 * x
>>> triple = trace(triple)
``````