提问者:小点点

与函数式编程中的“折叠”函数等价的“pythonic”是什么?


在 Haskell 中实现以下目标的最惯用方法是什么:

foldl (+) 0 [1,2,3,4,5]
--> 15

或者在 Ruby 中等效:

[1,2,3,4,5].inject(0) {|m,x| m + x}
#> 15

显然,Python提供了reduce函数,它是折叠的实现,与上面完全一样,但是,我被告知“pythonic”编程方式是避免lambda术语和高阶函数,在可能的情况下更喜欢列表推导。因此,是否有一种折叠列表的首选方法,或者 Python 中类似列表的结构不是 reduce 函数,或者 reduce 是实现此目的的惯用方式


共3个答案

匿名用户

对数组求和的 Pythonic 方法是使用 sum。对于其他目的,您有时可以使用reduce(来自functools模块)和运算符模块的某种组合,例如:

def product(xs):
    return reduce(operator.mul, xs, 1)

请注意,用Haskell的术语来说,< code>reduce实际上是一个< code>foldl。没有特殊的语法来执行折叠,没有内置的< code>foldr,实际上使用< code>reduce和非关联操作符被认为是不好的风格。

使用高阶函数是一种非常单调的行为;它很好地利用了Python的原理,即一切都是一个对象,包括函数和类。你说的没错,一些Pythonista不喜欢lambdas,但主要是因为当它们变得复杂时,它们往往不太可读。

匿名用户

Python 3.8 开始,引入赋值表达式 (PEP 572) (:= 运算符),它提供了命名表达式结果的可能性,我们可以使用列表推导来复制其他语言称为折叠/折叠左/减少操作:

给定一个列表、一个归约函数和一个累加器:

items = [1, 2, 3, 4, 5]
f = lambda acc, x: acc * x
accumulator = 1

我们可以用 f 折叠项目以获得结果累积

[accumulator := f(accumulator, x) for x in items]
# accumulator = 120

或以浓缩的形式:

acc = 1; [acc := acc * x for x in [1, 2, 3, 4, 5]]
# acc = 120

注意,这实际上也是一个“scanleft”操作,因为列表理解的结果代表了每一步的累积状态:

acc = 1
scanned = [acc := acc * x for x in [1, 2, 3, 4, 5]]
# scanned = [1, 2, 6, 24, 120]
# acc = 120

匿名用户

哈斯克尔

< code>foldl ( ) 0 [1,2,3,4,5]

蟒蛇

< code>reduce(lambda a,b: a b,[1,2,3,4,5],0)

显然,这是说明一个观点的一个微不足道的例子。在Python中,你只需要做< code>sum([1,2,3,4,5]),甚至Haskell纯粹主义者通常更喜欢< code>sum [1,2,3,4,5]。

对于没有明显便利函数的重要场景,惯用的pythonic方法是显式写出For循环,并使用可变变量赋值,而不是使用< code>reduce或< code>fold。

这根本不是功能性的风格,而是“蟒蛇式”的风格。Python不是为功能纯粹主义者设计的。了解Python如何支持流控制的异常,以了解Python的非功能惯用用法。