Python中的高效功能列表迭代

时间:2016-02-16 13:47:21

标签: python haskell functional-programming

假设我有一些元素的数组。每个元素都有一些属性。

我需要从谓词确定的某些值子集中筛选此列表。这个子集当然可以有交叉点。 我还需要确定每个这样的子集中的值的数量。

因此,使用命令式方法,我可以编写类似的代码,它的运行时间为2 * n。复制数组的一次迭代和另一次迭代来计算子集大小。

from split import import groupby

a = [{'some_number': i, 'some_time': str(i) + '0:00:00'} for i in range(10)]


# imperative style

wrong_number_count = 0
wrong_time_count = 0

for item in a[:]:
    if predicate1(item):
        delete_original(item, a)
        wrong_number_count += 1

    if predicate2(item):
        delete_original(item, a)
        wrong_time_count += 1

    update_some_data(item)

do_something_with_filtered(a)


def do_something_with_filtered(a, c1, c2):
    print('filtered a {}'.format(a))
    print('{} items had wrong number'.format(c1))
    print('{} items had wrong time'.format(c2))


def predicate1(x):
    return x['some_number'] < 3


def predicate2(x):
    return x['some_time'] < '50:00:00'

不知何故,我无法想象在Python中以相同的运行时间以功能方式执行此操作的方法。 所以在功能风格上我可能多次使用groupby或者为每个谓词编写一个理解,但这显然比命令式方法慢。

我认为使用Stream Fusion在Haskell中可能有这样的事情(我是对的吗?) 但是在Python中怎么做?

2 个答案:

答案 0 :(得分:0)

是的,Haskell中的融合通常会将写入两次传递的内容转换为单次传递。虽然在列表的情况下,它实际上是折叠/构建融合而不是流融合。

但是,在不强制执行纯度的语言中,这通常是不可能的。当涉及副作用时,将多个通道融合为一个不再正确。如果每次传球都输出了怎么办?未融合,您将分别获得每个传递的所有输出。融合,你得到两个传递交错的输出。

如果您承诺只使用纯函数,那么可以在Python中编写融合式框架,该框架将正常工作。但我很怀疑此刻存在这样的事情。 (不过,我很想被证明是错的。)

答案 1 :(得分:0)

Python以其迭代器的形式对“流处理”提供了强有力的支持 - 而且你要求seens只是微不足道。您只需要有一种方法可以将谓词和属性分组到它 - 它可以是一个字典,其中谓词本身就是键。

也就是说,一个简单的迭代器函数接收你的谓词数据结构,以及要处理的数据可以做你想要的。迭代器会产生使用谓词信息更改数据结构的副作用。如果你想要“纯函数”,你只需要复制谓词信息,并且可能为每个元素传递和检索所有谓词和计数器值到迭代器(通过send方法) - 我不需要认为这样的纯粹主义是值得的。

那说你可以随身携带你的代码:

from collections import OrderedDict


def predicate1(...):
    ...

...

def preticateN(...):
    ...

def do_something_with_filtered(item):
    ...

def multifilter(data, predicates):
    for item in data:
        for predicate in predicates:
            if predicate(item):
                predicates[predicate] += 1
                break
        else:
            yield item


def do_it(data):
    predicates = OrderedDict([(predicate1, 0), ..., (predicateN, 0)  ])
    for item in multifilter(data, predicates):
        do_something_with_filtered(item)

    for predicate, value in predicates.items():
        print("{} filtered out {} items".format(predicate.__name__, value)


a = ...
do_it(a)

(如果你必须为所有谓词计算一个项失败,那么从“break”语句到状态标志变量的明显变化就足够了)