使用递归函数将嵌套列表转换为Set

时间:2016-03-22 21:19:18

标签: python performance python-3.x recursion time

我需要创建一个递归函数(为简单起见),它接受任何嵌套列表并返回一组唯一元素。

为了解决这个问题,我决定首先创建一个获取列表并将其转换为集合的函数:

ranList = [2, 2, 4, 5, 3, 1, 3]

def eue(ranList):
    newList = set(ranList)
    return print(newList)

很简单,它有效。现在创建一个函数,它接受一个嵌套列表并返回一个二维列表(这是我在这个站点上使用搜索找到的递归函数)和一个接受另一个函数的函数,并返回一个包含唯一元素的集合: p>

lis = [['c', 'd'], 2, 2, 4, 5, 3, 1, 3, ['c']]

from collections import Iterable

def flatten(lis):
    for item in lis:
        if isinstance(item, Iterable) and not isinstance(item, str):
            for x in flatten(item):
                yield x
        else:        
            yield item

def eue(lis):
    newSet = set(flatten(lis))
    print(newSet)

现在,通过调用eue(),它解决了我原来的问题。但我想让它变得更加简单化。

如何组合这些函数来生成单个函数,以减少运行所需的计算时间?

感谢。

1 个答案:

答案 0 :(得分:1)

您可以使用itertools.chain将所有迭代器链接在一起:

from itertools import chain
from collections import Iterable

def isIter(obj):
    return isinstance(obj, Iterable) and not isinstance(obj,str)
def flatten(it):
    seqs = (flatten(item) if isIter(item) else (item,) for item in it)
    return chain(*seqs)
def enu1(it):
    return set(gen(it))

虽然这意味着对于每个非序列元素,您必须使用该项(item,)创建元组以使链正常工作,但不确定它对性能的影响有多大。

您可以通过转换为集合将其减少为单个函数(isIter除外):

def enu2_A(it):
    seqs = (enu2_A(item) if isIter(item) else (item,) for item in it)
    return set(chain(*seqs))

但是这又为set的每次递归调用创建了一个新的enu对象,可能会添加一个转换选项吗?

def enu2(it,return_set = True):
    seqs = (enu2(item,False) if isIter(item) else (item,) for item in it)
    if return_set:
        return set(chain(*seqs))
    else:
        return chain(*seqs)

但将它组合成一个单独的功能实际上并没有提供太多的速度提升:

import timeit

a = timeit.timeit("enu1(lis)","from __main__ import enu1,lis",number=10000)
b = timeit.timeit("enu2(lis)","from __main__ import enu2,lis",number=10000)

print(a,b)
print(a/b) #ratio, more then 1 means a took longer

输出:

0.3400449920009123 0.32908301999850664
1.0333106582115827

通过组合成一个单独的功能,快3%,我猜这不是你期望的速度,你的代码是非常有效的,因为它是更加pythonesque然后我的所以我不会改变它

编辑:刚刚对我的enu2和你的enu进行了基准测试 - 你的方法比我提供的方法快了大约16%,请保留它是因为你不能变得更好,然后转移到python2并使用compiler.ast.flatten或另一个C级等效:

from collections import Iterable

def flatten(lis):
    for item in lis:
        if isinstance(item, Iterable) and not isinstance(item, str):
            for x in flatten(item):
                yield x
        else:        
            yield item

def enu(obj):
    return set(flatten(obj))

lis = [['c', 'd'], 2, 2, 4, 5, 3, 1, 3, ['c']]

import timeit

a = timeit.timeit("enu(lis)","from __main__ import enu,lis",number=10000)

b = timeit.timeit("set(ast.flatten(lis))","from __main__ import lis ; from compiler import ast",number=10000)

print(a,b)
print(a/b)

输出:

0.3324121500045294 0.28561264199379366
1.1638565705076622

所以在C中执行操作加快了4X的过程,但是根据the docscompiler包已经过时了2.6:

  

从2.6版开始不推荐使用:Python 3中已删除了编译器包。

所以很有可能如果你在C扩展中编写flatten你可以获得更好的结果,但是如果你想用纯python编写代码,那么你可以获得尽可能好的代码。