P114: Duplicate the elements of a list
重复列表中的每一个元素。照例还是先写测试用例:
from python99.lists.p114 import duplicate
def test_duplicate():
assert duplicate([]) == []
assert duplicate([1]) == [1, 1]
assert duplicate([1, 1, 2, 3]) == [1, 1, 1, 1, 2, 2, 3, 3]
这题用MapReduce范式来解会很清晰。MapReduce中的主要概念「Map(对映)」和「Reduce(归纳)」是从函数式程序设计语言借来的。简单来说,一个对映函数就是对列表上每一个元素进行指定的操作,并将结果存储于新的列表中。而归纳操作则是对一个列表的元素进行适当的合併。
本例中,map
就是把单个元素对映成包含两个相同元素的列表,reduce
则是把以上所有的列表都拼接成一个列表。举个例子,给定列表[1, 2, 3, 4]
。首先,将列表中每一个元素都对映成包含两个相同元素的列表。然后,把所有的列表都拼接起来。
代码实现:
# Duplicate the elements of a list
import functools
import operator
def duplicate(l):
return functools.reduce(operator.concat, [[e] * 2 for e in l], [])
Python内建的List comprehension用一种简洁的方式实现了「Map(对映)」。标准库中的functools
则提供了「Reduce(归纳)」的实现。
List comprehension的形式为:
[f(x) for x in l]
f
为对映函数,其可为普通Python函数,也可为表达式x
为局部变量,用于引用每一个元素l
输入列表
本例中,f
为[x] * 2
,将单个元素转换为包含两个相同元素的列表。
functools.reduce
的源代码为:
_initial_missing = object()
def reduce(function, sequence, initial=_initial_missing):
"""
reduce(function, sequence[, initial]) -> value
Apply a function of two arguments cumulatively to the items of a sequence,
from left to right, so as to reduce the sequence to a single value.
For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
((((1+2)+3)+4)+5). If initial is present, it is placed before the items
of the sequence in the calculation, and serves as a default when the
sequence is empty.
"""
it = iter(sequence)
if initial is _initial_missing:
try:
value = next(it)
except StopIteration:
raise TypeError("reduce() of empty sequence with no initial value") from None
else:
value = initial
for element in it:
value = function(value, element)
return value
try:
from _functools import reduce
except ImportError:
pass
reduce
返回值应与initial
同类型。function
接受两个参数,第一个应与initial
和reduce
返回值同类型,第二个是sequence
里的元素。
本例中,initial
是空列表[]
。sequence
的每个元素都是包含两个相同元素的列表。function
接受的两个参数都是无嵌套的列表,并将它们拼接成一个无嵌套的列表。