Pythonic累积图

时间:2018-03-07 02:13:12

标签: python python-3.x

是否有更多的pythonic方式来执行以下操作:

def mysteryFunction( it, fun, val ):
    out = []
    for x in it:
        y,val = fun(x,val)
        out.append(y)
    return out,val

其中it是可迭代的,fun是一个函数,它接受两个输入并返回两个输出,而val是一个初始值,每次调用{{{ 1}}?

我在问,因为我经常使用fun和列表理解,但是我无法将之前的功能表达为这些功能的组合,而这已经出现了好几次了。我错过了一个隐藏的习语,或者这只是一个适合自己的利基?

一个具体的例子是从一定的秒数计算(年,周,日,小时,分钟,秒)的持续时间:

map, zip, filter, reduce

其中fac = (365*24*3600, 7*24*3600, 24*3600, 3600, 60, 1) dur,rem = mysteryFunction( fac, lambda x,y: divmod(y,x), 234567 ) 是持续时间元组,dur对应于最终余数(此处为零或小数,具体取决于初始值的类型)。这不仅仅是挑选,还有许多其他的例子,例如:整合微分方程的固定步骤方法(可迭代步骤,步进函数,初始状态);模拟有界随机游走;深度的树处理没有递归;等

1 个答案:

答案 0 :(得分:6)

此结构与the itertools.accumulate generator function的设计类似。例如,您的函数可能与以下函数一起使用:

def add2(x, y):
    return (x + y,) * 2 # Returning the same thing twice, new sum and accumulate sum the same
然后打电话给:

mysteryFunction(range(5), add2, 0)

会返回:

([0, 1, 3, 6, 10], 10)

累计总和0到4,以及最终总和。

itertools.accumulate可以做同样的事情,但它是懒惰的(它按照请求返回每个累加值),并且只适用于两个操作数到单个输出函数;对于这种情况,它最终变得更简单:

from itertools import accumulate
from operator import add

list(accumulate(range(5), add))

将生成与list相同的mystery_function(第二个结果只是list的最后一个值),但您也可以懒惰地使用它而不将结果存储在a list,例如:

for partialsum in accumulate(range(5), add):
    ... do stuff with partialsum ...

您可以按摩accumulate来处理两个输入,两个输出功能(或者更准确地说,从accumulate输出的值中丢弃您不关心的值),但大多数情况下我希望第二个输出是迄今为止累积的值,而不是真正分开,所以避免第二个输出会更清晰。

为了好玩,对你的结构进行一种可怕的按摩来匹配accumulate。假设您想为输入中的每个元素添加base值,但每次将base减少1。使用您的功能,您可以(初始base为10):

def addless(new, base):
    return base + new, base - 1

mysteryFunction(range(5), addless, 10)

(感谢传递它range抵消base中的每次减少)产生([10, 10, 10, 10, 10], 5)。与accumulate类似的代码可能是:

def addless2(last_base, new):
    _, base = last_base
    return base + new, base - 1

然后(因为你不能直接指定accumulate的初始值而有些丑陋):

from itertools import accumulate, chain

base = 10

# chain used to provide initial value
accum = accumulate(chain(((None, base),), range(5)), addless2)

next(accum)   # Throw away first value that exists solely to make initial tuple

# Put the first value from each `tuple` in `out`, and keep the second value
# only for the last output, to preserve the final base
out, (*_, base) = zip(*accum)

vals作为(10, 10, 10, 10, 10)base作为5,就像在您的代码中一样(为魔术道歉; zip使用通用的嵌套解包一下子既漂亮又可怕。)