简体   繁体   English

在python中等效的Haskell scanl

[英]Equivalent of Haskell scanl in python

I would like to know if there is a built in function in python for the equivalent Haskell scanl , as reduce is the equivalent of foldl . 我想知道python中是否有内置函数用于等效的Haskell scanl ,因为reduce相当于foldl

Something that does this: 这样做的东西:

Prelude> scanl (+) 0 [1 ..10]
[0,1,3,6,10,15,21,28,36,45,55]

The question is not about how to implement it, I already have 2 implementations, shown below (however, if you have a more elegant one please feel free to show it here). 问题不是如何实现它,我已经有2个实现,如下所示(但是,如果你有一个更优雅的实现,请随时在这里显示)。

First implementation: 首次实施:

 # Inefficient, uses reduce multiple times
 def scanl(f, base, l):
   ls = [l[0:i] for i in range(1, len(l) + 1)]
   return [base] + [reduce(f, x, base) for x in ls]

  print scanl(operator.add, 0, range(1, 11))

Gives: 得到:

[0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55]

Second implementation: 第二次实施:

 # Efficient, using an accumulator
 def scanl2(f, base, l):
   res = [base]
   acc = base
   for x in l:
     acc = f(acc, x)
     res += [acc]
   return res

 print scanl2(operator.add, 0, range(1, 11))

Gives: 得到:

[0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55]

Thank you :) 谢谢 :)

You can use this, if its more elegant: 你可以使用它,如果它更优雅:

def scanl(f, base, l):
    for x in l:
        base = f(base, x)
        yield base

Use it like: 使用它像:

import operator
list(scanl(operator.add, 0, range(1,11)))

Python 3.x has itertools.accumulate(iterable, func= operator.add) . Python 3.x有itertools.accumulate(iterable, func= operator.add) It is implemented as below. 它的实现如下。 The implementation might give you ideas: 实施可能会给你一些想法:

def accumulate(iterable, func=operator.add):
    'Return running totals'
    # accumulate([1,2,3,4,5]) --> 1 3 6 10 15
    # accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120
    it = iter(iterable)
    total = next(it)
    yield total
    for element in it:
        total = func(total, element)
        yield total

I had a similar need. 我有类似的需求。 This version uses the python list comprehension 这个版本使用python列表理解

def scanl(data):
    '''
    returns list of successive reduced values from the list (see haskell foldl)
    '''
    return [0] + [sum(data[:(k+1)]) for (k,v) in enumerate(data)]


>>> scanl(range(1,11))

gives: 得到:

[0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55]

Starting Python 3.8 , and the introduction of assignment expressions (PEP 572) ( := operator), which gives the possibility to name the result of an expression, we can use a list comprehension to replicate a scan left operation: 启动Python 3.8 ,并引入赋值表达式(PEP 572):=运算符),它可以命名表达式的结果,我们可以使用列表推导来复制扫描左操作:

acc = 0
scanned = [acc := acc + x for x in [1, 2, 3, 4, 5]]
# scanned = [1, 3, 6, 10, 15]

Or in a generic way, given a list, a reducing function and an initialized accumulator: 或者以通用方式,给定列表,缩减函数和初始化累加器:

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

we can scan items from the left and reduce them with f : 我们可以从左边扫描items并用f减少它们:

scanned = [accumulator := f(accumulator, x) for x in items]
# scanned = [1, 3, 6, 10, 15]

As usual, the Python ecosystem is also overflowing with solutions: 像往常一样,Python生态系统也充满了解决方案:

Toolz has an accumulate capable of taking a user-supplied function as an argument. Toolz有一个能够将用户提供的函数作为参数的累加。 I tested it with lambda expressions. 我用lambda表达式测试了它。

https://github.com/pytoolz/toolz/blob/master/toolz/itertoolz.py https://github.com/pytoolz/toolz/blob/master/toolz/itertoolz.py

https://pypi.python.org/pypi/toolz https://pypi.python.org/pypi/toolz

as does more_itertools 和more_itertools一样

http://more-itertools.readthedocs.io/en/stable/api.html http://more-itertools.readthedocs.io/en/stable/api.html

I did not test the version from more-itertools, but it also can take a user-supplied function. 我没有测试更多的itertools版本,但它也可以采用用户提供的功能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM