什么是“ pythonic”等价于函数式编程中的“ old”函数?

在哈斯克尔,实现以下目标的最惯用方法是什么:

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是实现这一点的惯用方法?

134467 次浏览

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

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

请注意,用哈斯克尔的术语来说,reduce实际上是 foldl。没有特殊的语法来执行折叠,没有内置的 foldr,实际上使用带有非关联运算符的 reduce被认为是糟糕的风格。

使用高阶函数非常勉强; 它很好地利用了 Python 的原则,即所有东西都是一个对象,包括函数和类。你是对的,lambdas 被一些 Python 工作者所反对,但主要是因为当它们变得复杂时,它们往往不是很易读。

这个(reduce)问题的实际答案是: 只需使用一个循环!

initial_value = 0
for x in the_list:
initial_value += x #or any function.

这将比 reduce 更快,类似 PyPy 的东西可以优化这样的循环。

顺便说一下,和的情况应该用 sum函数来解决

你也可以重新发明轮子:

def fold(f, l, a):
"""
f: the function to apply
l: the list to fold
a: the accumulator, who is also the 'zero' on the first call
"""
return a if(len(l) == 0) else fold(f, l[1:], f(a, l[0]))


print "Sum:", fold(lambda x, y : x+y, [1,2,3,4,5], 0)


print "Any:", fold(lambda x, y : x or y, [False, True, False], False)


print "All:", fold(lambda x, y : x and y, [False, True, False], True)


# Prove that result can be of a different type of the list's elements
print "Count(x==True):",
print fold(lambda x, y : x+1 if(y) else x, [False, True, True], 0)

Haskell

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

巨蟒

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

显然,这是一个简单的例子来说明一个问题。在 Python 中,您只需要执行 sum([1,2,3,4,5]),甚至 Haskell 纯粹主义者通常也更喜欢 sum [1,2,3,4,5]

对于没有明显便利函数的非平凡场景,惯用的 pythonic 方法是显式写出 For 循环,并使用变量赋值,而不是使用 reducefold

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

实际上并不是这个问题的答案,而是 foldl 和 foldr 的一行代码:

a = [8,3,4]


## Foldl
reduce(lambda x,y: x**y, a)
#68719476736


## Foldr
reduce(lambda x,y: y**x, a[::-1])
#14134776518227074636666380005943348126619871175004951664972849610340958208L

在 Python3中,已经删除了 reduce: 发布说明

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

另一方面,文档表示倾向于使用 for-loop 而不是 reduce,因此:

def product(xs):
result = 1
for i in xs:
result *= i
return result

我相信这个问题的一些回答者忽略了 fold函数作为一个抽象工具的更广泛含义。是的,sum可以对整数列表执行同样的操作,但这是一个小例子。fold更为通用。当您有一系列形状各异的数据结构并希望清楚地表示聚合时,它非常有用。因此,与其用一个聚合变量构建一个 for循环,然后每次手动重新计算它,不如用一个 fold函数(或者 Python 版本,reduce看起来对应的那个版本) ,让程序员通过简单地提供两件事情,就可以更清楚地表达聚合的意图:

  • 聚合的默认起始值或“种子”值。
  • 一个函数,它接受聚合的当前值(从“种子”开始)和列表中的下一个元素,并返回下一个聚合值。

Python 3.8开始,引入了 赋值表达式(PEP 572)(:=操作符) ,这使得命名一个表达式的结果成为可能,我们可以使用一个列表内涵来复制其他语言所谓的 foldleft/reduce 操作:

给定一个列表、一个递减函数和一个累加器:

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

我们可以将 itemsf折叠以得到 accumulation:

[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