高阶函数是指能够接受其他函数作为参数或者返回函数作为结果的函数。在函数式编程中,高阶函数是一种强大的工具,它使得代码更加灵活、抽象和可复用。
匿名函数是指在定义时不显式命名的函数,通常用于一次性的、简单的操作。在函数式编程中,匿名函数也被称为lambda表达式。
关键字lambda
表示匿名函数,冒号前面的x
表示函数参数。
匿名函数有个限制,就是只能有一个表达式,不用写return
,返回值就是该表达式的结果。
用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数
lambda parameters: expression
add = lambda x, y: x + y
result = add(3, 5)
print(result) # 输出 8
def build(x, y):
return lambda: x * x + y * y
map()
函数接收两个参数,一个是函数,一个是Iterable
,map
将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator
返回。
def f(x):
return x * x
r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
list(r)
map()
传入的第一个参数是f
,即函数对象本身。由于结果r
是一个Iterator
,Iterator
是惰性序列,因此通过list()
函数让它把整个序列都计算出来并返回一个list。
可迭代对象(Iterable):
- 可迭代对象是指实现了
__iter__()
方法的对象,或者实现了__getitem__()
方法的对象。- 可迭代对象可以被用于构建迭代器,它是一个容器,可以通过迭代获取其中的元素。
- 示例:列表、元组、字符串等都是可迭代对象。
my_list = [1, 2, 3] for item in my_list: print(item)
迭代器(Iterator):
- 迭代器是可迭代对象的一种实现方式,它不仅实现了
__iter__()
方法,还实现了__next__()
方法。- 迭代器通过
__next__()
方法逐一返回可迭代对象中的元素,当没有元素可返回时,抛出StopIteration
异常。- 示例:使用
iter()
函数可以将可迭代对象转换为迭代器。my_iter = iter(my_list) print(next(my_iter)) # 输出 1 print(next(my_iter)) # 输出 2
总结区别:
- 可迭代对象是具有
__iter__()
方法的对象,而迭代器是同时实现了__iter__()
和__next__()
方法的对象。- 可迭代对象可以通过
iter()
函数转换为迭代器,然后使用next()
函数逐一获取元素。- 迭代器是一种实现了延迟计算的机制,只在需要时才生成下一个元素,节省内存。
在实际编程中,很多情况下我们使用for循环来迭代,而不直接操作迭代器。Python中的for循环实际上在内部调用了
iter()
函数,将可迭代对象转换为迭代器,并使用next()
方法逐一获取元素。
reduce
把一个函数作用在一个序列[x1, x2, x3, ...]
上,这个函数必须接收两个参数,reduce
把结果继续和序列的下一个元素做累积计算。
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
from functools import reduce
def add(x, y):
return x + y
reduce(add, [1, 3, 5, 7, 9])
[1, 3, 5, 7, 9]
变换成整数13579
from functools import reduce
def fn(x, y):
return x * 10 + y
reduce(fn, [1, 3, 5, 7, 9])
str
转换为int
的函数from functools import reduce
def fn(x, y):
return x * 10 + y
def char2num(s):
digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
return digits[s]
reduce(fn, map(char2num, '13579'))
from functools import reduce
DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
def str2int(s):
def fn(x, y):
return x * 10 + y
def char2num(s):
return DIGITS[s]
return reduce(fn, map(char2num, s))
from functools import reduce
DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
def char2num(s):
return DIGITS[s]
def str2int(s):
return reduce(lambda x, y: x * 10 + y, map(char2num, s))
filter()
也接收一个函数和一个序列。和map()
不同的是,filter()
把传入的函数依次作用于每个元素,然后根据返回值是True
还是False
决定保留还是丢弃该元素。
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
def not_empty(s):
return s and s.strip()
list(filter(not_empty, ['A', '', 'B', None, 'C', ' ']))
# 结果: ['A', 'B', 'C']
可见用filter()
这个高阶函数,关键在于正确实现一个“筛选”函数。
注意到
filter()
函数返回的是一个Iterator
,也就是一个惰性序列,所以要强迫filter()
完成计算结果,需要用list()
函数获得所有结果并返回list。
计算素数的一个方法是埃氏筛法,它的算法理解起来非常简单:
首先,列出从2
开始的所有自然数,构造一个序列:
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
取序列的第一个数2
,它一定是素数,然后用2
把序列的2
的倍数筛掉:
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
取新序列的第一个数3
,它一定是素数,然后用3
把序列的3
的倍数筛掉:
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
取新序列的第一个数5
,然后用5
把序列的5
的倍数筛掉:
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
不断筛下去,就可以得到所有的素数。
先构造一个从3
开始的奇数序列
def _odd_iter():
n = 1
while True:
n = n + 2
yield n
注意这是一个生成器,并且是一个无限序列。
然后定义一个筛选函数
def _not_divisible(n):
return lambda x: x % n > 0
最后,定义一个生成器,不断返回下一个素数
def primes():
yield 2
it = _odd_iter() # 初始序列
while True:
n = next(it) # 返回序列的第一个数
yield n
it = filter(_not_divisible(n), it) # 构造新序列
这个生成器先返回第一个素数2
,然后,利用filter()
不断产生筛选后的新的序列。
由于primes()
也是一个无限序列,所以调用时需要设置一个退出循环的条件:
# 打印1000以内的素数:
for n in primes():
if n < 1000:
print(n)
else:
break
注意到Iterator
是惰性计算的序列,所以我们可以用Python表示“全体自然数”,“全体素数”这样的序列,而代码非常简洁
迭代器(Iterator):
- 迭代器是一个具有
__iter__()
和__next__()
方法的对象。__iter__()
返回迭代器对象本身,而__next__()
用于获取迭代器中的下一个元素。- 当没有元素可返回时,
__next__()
方法应该抛出StopIteration
异常,通知迭代已经结束。- 示例:使用
iter()
函数可以将可迭代对象转换为迭代器,并使用next()
函数逐一获取元素。my_iter = iter([1, 2, 3]) print(next(my_iter)) # 输出 1 print(next(my_iter)) # 输出 2
生成器(Generator):
- 生成器是一种特殊的迭代器,可以通过函数和
yield
关键字创建。- 函数中包含
yield
语句的时候,它不是普通的函数,而是一个生成器函数。- 生成器函数每次调用
yield
时,会暂停执行并将结果返回给调用者,保持函数状态,等待下一次调用。- 示例:使用生成器函数创建生成器。
def my_generator(): yield 1 yield 2 yield 3 gen = my_generator() print(next(gen)) # 输出 1 print(next(gen)) # 输出 2
区别
- 迭代器是一种实现了
__iter__()
和__next__()
方法的对象,可以通过iter()
函数和next()
函数进行迭代。- 生成器是一种特殊的迭代器,通过生成器函数和
yield
语句实现,更方便、简洁地创建迭代器。- 生成器具有惰性计算特性,只在需要时才生成下一个元素,节省内存。
- 迭代器和生成器都是用于处理可迭代对象的工具,使得在处理大数据集时能够更有效地进行迭代操作。
参考链接:
MapReduce: Simplified Data Processing on Large Clusters
map/reduce - 廖雪峰的官方网站