映射/减少具有多个for子句的列表推导的等效项

时间:2014-02-28 09:40:47

标签: python map functional-programming list-comprehension fold

我想仅使用高阶函数编写functional等效的列表推导,并且没有副作用。我这样做是出于严格的学习目的。 我知道列表推导是Pythonic。在Python中map(f, xs)相当于[f(x) for x in xs]。但是下面这些的等价物是什么?

  • A:[f(x, y) for x in xs for y in ys]
  • B:[f(x, y) for x in range(1, 5) for y in range(x, 5)]

map仅返回相同长度的列表。 reduce更为通用,您可以在其上实施mapfilter

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更加棘手,因为1st for子句的迭代变量在第2个子句中使用。如何编写实现列表推导功能的函数(或函数集)?

2 个答案:

答案 0 :(得分:13)

这是monad的模式,特别是列表monad。在许多语言中,monad隐藏在某种语法糖之后,例如C#的LINQ,Scala的sequence comprehensions,Haskell的do-notation,或者甚至更多的语言,(多)列表推导(像这里的Python)。

从任何这些含糖语法转换为普通函数的关键术语是(在列表的特殊情况下)类型([a], a -> [b]) -> [b]的函数,它是monad定义的基本部分。该功能以不同的名称已知,例如(>>=)或“绑定”,flatMapconcatMapselectMany

对于列表的情况,concatMapflatMap可能是最好的名称,因为它的作用是:在列表上映射一个返回列表的函数,给出一个列表列表;然后,压扁该列表。


现在有更具体的内容 1

> from functools import reduce
> from operator import add
> def concatMap(xs, f):
      return reduce(add, map(f, xs), []) # only map and reduce!

测试:

> [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是不够的 - 实际需要的是一个广义的“展平”操作{ {3}},类型为m<m<a>>,(使用模板/泛型语法),其中m是相关monad的类型。

1 如评论中所述,这也可以定义为concatMap = lambda xs, f: chain.from_iterable(map(f, xs)),使用itertools和身份(>>=) ≡ join . fmap

答案 1 :(得分:2)

您可以使用itertools.starmapitertools.product来查看案例 A

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']