Python 具有多个for子句的列表理解的Map/reduce等价项

Python 具有多个for子句的列表理解的Map/reduce等价项,python,map,functional-programming,list-comprehension,fold,Python,Map,Functional Programming,List Comprehension,Fold,我想写一个等效的列表理解,只使用高阶函数,没有副作用。我这样做纯粹是为了学习。我知道列表理解是python式的。在Python中,map(f,xs)相当于[f(x)表示xs中的x]。但是下面这些的等价物是什么 A:[f(x,y)表示x在xs中表示y在ys中表示y] B:[f(x,y)表示范围(1,5)中的x,y表示范围(x,5)] map仅返回相同长度的列表reduce更为通用,您可以在其上实现map和filter map(f, xs) == reduce(lambda a, e: a +

我想写一个等效的列表理解,只使用高阶函数,没有副作用。我这样做纯粹是为了学习。我知道列表理解是python式的。在Python中,map(f,xs)相当于
[f(x)表示xs中的x]
。但是下面这些的等价物是什么

  • A:
    [f(x,y)表示x在xs中表示y在ys中表示y]
  • B:
    [f(x,y)表示范围(1,5)中的x,y表示范围(x,5)]
map
仅返回相同长度的列表
reduce
更为通用,您可以在其上实现
map
filter

map(f, xs) == reduce(lambda a, e: a + [f(e)], xs, [])
filter(p, xs) == reduce(lambda a, e: a + [e] if p(e) else a, xs, [])
因此,A可以实现为:

def map2(f, xs, ys):
    reduce(lambda a, x: a + map(lambda y: f(x, y), ys), xs, [])
但对于子句,这并不能推广到>2。而B则更为棘手,因为在第二个子句中使用了第一个for子句中的迭代变量。如何编写实现列表理解功能的函数(或函数集)?

您可以使用,对于casea

from itertools import product, starmap
list(starmap(f, product(xs, ys)))
演示:

>>> from operator import mul
>>> [mul(x, y) for x in range(1, 4) for y in 'abc']
['a', 'b', 'c', 'aa', 'bb', 'cc', 'aaa', 'bbb', 'ccc']
>>> list(starmap(mul, product(range(1, 4), 'abc')))
['a', 'b', 'c', 'aa', 'bb', 'cc', 'aaa', 'bbb', 'ccc']
这是a的模式,特别是列表单子。在许多语言中,单子隐藏在某种语法糖的后面,例如C#、Scala、Haskell,或者在更多语言中,(多)列表理解(如Python中)

将这些含糖语法转换为普通函数的关键术语是(在列表的特殊情况下)类型为
([a],a->[b])->[b]
的函数,这是单子定义的基本部分。此函数以不同的名称命名,例如,或“bind”,或
concatMap
,或

对于列表而言,
concatMap
flatMap
可能是最好的名称,因为它就是这样做的:将返回列表的函数映射到一个列表上,给出一个列表列表;然后,将该列表展平


现在来了解更具体的情况1:

测试:

> [x*y for x in range(1 ,5) for y in range(x, 5)]
> [1, 2, 3, 4, 4, 6, 8, 9, 12, 16]
> concatMap(range(1, 5), lambda x: concatMap(range(x, 5), lambda y:[x*y]))
> [1, 2, 3, 4, 4, 6, 8, 9, 12, 16]
更有趣的是:

> [x*y+z for x in range(1, 5) for y in range(x, 5) for z in range(x, y)]
> [3, 4, 5, 5, 6, 7, 8, 10, 11, 15]
> concatMap(range(1, 5),lambda x: concatMap(range(x, 5), lambda y: concatMap(range(x, y),lambda z: [x*y+z])))
> [3, 4, 5, 5, 6, 7, 8, 10, 11, 15]

最后,需要注意的是,尽管monad总是需要类似于map的函数,但一般来说,reduce是不够的——实际上需要的是一个广义的“展平”操作,类型类似于
m
(使用模板/泛型语法),其中
m
是所讨论的单子类型


1如注释中所述,这也可以定义为
concatMap=lambda xs,f:chain.from_iterable(map(f,xs))
,使用
itertools
和标识
(>>=)≡ 参加fmap

我将使用itertools.chain.from\u iterable代替建议的concatMap。bind的等价物是lambda f,它是:itertools.chain.from_iterable(itertools.starmap(f,it))
> [x*y+z for x in range(1, 5) for y in range(x, 5) for z in range(x, y)]
> [3, 4, 5, 5, 6, 7, 8, 10, 11, 15]
> concatMap(range(1, 5),lambda x: concatMap(range(x, 5), lambda y: concatMap(range(x, y),lambda z: [x*y+z])))
> [3, 4, 5, 5, 6, 7, 8, 10, 11, 15]