【问题标题】:Map/reduce equivalent for a list comprehension with multiple for clauses具有多个 for 子句的列表理解的映射/减少等效项
【发布时间】:2014-04-01 05:34:52
【问题描述】:

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

  • 答:[f(x, y) for x in xs for y in ys]
  • 乙:[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 for 子句。而 B 则更加棘手,因为第一个 for 子句中的迭代变量用于第二个子句。如何编写实现列表理解功能的函数(或函数集)?

【问题讨论】:

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


    【解决方案1】:

    您可以使用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']
    

    【讨论】:

      【解决方案2】:

      这是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 是不够的——实际需要的是一个广义的“扁平化”操作join,与类似m<m<a>> 的类型(使用模板/泛型语法),其中m 是相关单子的类型。

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

      【讨论】:

      • 我会使用 itertools.chain.from_iterable 而不是建议的 concatMap。 bind的等价物是lambda f,它:itertools.chain.from_iterable(itertools.starmap(f, it))
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-07
      • 2012-08-18
      • 2012-07-01
      • 2023-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多