我正在仔细研究Mark Lutz' Learning Python, 5th Edition。在第20章(关于理解)中,Mark将内置函数(map
和filter
)与语法理解进行了比较。
例如,他比较
[x ** 2 for x in range(10)]
和
list(map((lambda x: x ** 2), range(10)))
...产生[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
的相同结果(第一个通过理解,第二个通过map
)。
然后,他仅以理解形式给出了第二个例子:
[以下]表达式将0到4之间的偶数与0到4之间的奇数组合。
if
子句在每次迭代中过滤掉项目:
[(x, y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1]
(...的输出为[(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]
。)
但Mark确实 使用map
/ filter
写出相同的功能,而不是给读者留下这个多汁的挑战:
这个最后一个例子的地图和过滤器相当复杂并且深度嵌套,所以我甚至不会尝试在这里展示它。我将把它的编码留作禅宗大师,前Lisp程序员和犯罪疯子的练习!
(这里的例子是一个很好的例子,以突出map
和理解之间的差异,这在许多其他情况下似乎非常相似,但在这种情况下在句法复杂性方面非常不同。)
我决定接受挑战,并根据map
和filter
来实现这种理解。我在下面提供了答案。
我的问题是,使用map
和filter
实现上述理解的方式是什么(可以说是最干净,最清晰,即使是丑陋的)?
答案 0 :(得分:2)
这是另一种方法(为便于阅读而格式化):
list(filter(
lambda pair: pair[0] % 2 == 0 and pair[1] % 2 == 1,
reduce(
lambda a, b: a+b,
list(map(
lambda x:
list(map(
lambda y: (x, y),
range(5)
)),
range(5)
))
)
))
(对于Python 3,假设您已完成from functools import reduce
。在Python 2中,它稍微简单一些,因为可以删除对list
的所有调用。)
很多复杂性来自于必须仅使用map / reduce来生成范围的笛卡尔积,这非常麻烦。如果您允许使用itertools
,事情会变得更加简单:
list(filter(
lambda pair: pair[0] % 2 == 0 and pair[1] % 2 == 1,
itertools.product(range(5), repeat=2)
))
答案 1 :(得分:1)
这是我最好的选择(似乎reduce
,以及lambda
,是必要的)(如果在Python而不是lambdas中绑定是可能的,我没有&t; t得到了本书的那一部分):
result = reduce(lambda r, g: r.extend(g) or r,
list(map(lambda x:
list(map(lambda y: (x, y),
filter(lambda a: a % 2 == 1, range(5)))),
filter(lambda b: b % 2 == 0, range(5)))),
[])
答案 2 :(得分:0)
这是解决方案:
f = map(lambda x:
list(map(lambda y:
(x, y), filter(lambda h:
h % 2 == 1, range(5))
)
),
filter(lambda g: g % 2 == 0, range(5))
)
print(list(f))
这是结果:
[[(0, 1), (0, 3)], [(2, 1), (2, 3)], [(4, 1), (4, 3)]]
注意:结果列表包括两个两次调用的双重括号。而且,如果两个双括号使您感到不安,则始终可以使用for循环跳过它。