Python实现可变串联运算符
澄清: 我刚刚意识到我下面的定义和代码可能是错误的,因为它们没有考虑嵌套列表。我真的希望,Python实现可变串联运算符,python,performance,recursion,optimization,concatenation,Python,Performance,Recursion,Optimization,Concatenation,澄清: 我刚刚意识到我下面的定义和代码可能是错误的,因为它们没有考虑嵌套列表。我真的希望,concatenate的最终结果要么是一个不是列表的对象,要么是一个多个不是列表的对象的列表(因此没有嵌套列表)。空列表应该变成空对象 但是用户可以提供由嵌套列表组成的输入,在这种情况下,我需要对它们进行删除。抱歉,我没有说清楚 我有某种类型的对象(也可以将空作为值),我在这些对象上有一个满足以下公理的二进制连接运算符(这里[a,B]表示包含a和B的列表): concatenate2(空,A)=conca
concatenate
的最终结果要么是一个不是列表的对象,要么是一个多个不是列表的对象的列表(因此没有嵌套列表)。空列表应该变成空对象
但是用户可以提供由嵌套列表组成的输入,在这种情况下,我需要对它们进行删除。抱歉,我没有说清楚 我有某种类型的对象(也可以将空作为值),我在这些对象上有一个满足以下公理的二进制连接运算符(这里[a,B]表示包含a和B的列表):
concatenate2(空,A)=concatenate2(A,空)=A
连接2(A[B,C])=连接2([A,B],C)=[A,B,C]
串联2(A,B)=[A,B](如果A,B与前面的任何情况不匹配)。
现在我还想得到任意多个术语的串联:
我希望以最小化列表复制操作数量的方式实现这些操作符,但我不确定如何在Python中做到最好
我现在的想法是这样做:
这看起来很好,很清晰,但我不知道它在性能和内存使用方面有多好。我担心在每次[…]+[…]操作期间可能会导致太多列表副本
有没有更好的方法来实施这些操作
请注意,实际上只需要执行
串联操作。concatenate2
运算符用于给出一个很好的递归定义,但是如果有人能够提出一个更有效的解决方案,而不使用它,我也会接受。使用+
进行重复连接并不理想,因为它会不断为每个二进制连接创建中间列表
对象,从而导致组合长度的二次最坏情况时间复杂度。一种更简单、更好的方法是具有线性复杂性的嵌套理解。
这还使用*
运算符来解包任意数量的参数:
def concatenate(*terms):
return [x for t in terms for x in (t if isinstance(t, list) else [t])]
>>> concatenate([3, 4], 5, [], 7, [1])
[3, 4, 5, 7, 1]
>>> concatenate()
[]
使用+
进行重复连接并不理想,因为它会不断为每个二进制连接创建中间列表
对象,从而导致组合长度的二次最坏情况时间复杂度。一种更简单、更好的方法是具有线性复杂性的嵌套理解。
这还使用*
运算符来解包任意数量的参数:
def concatenate(*terms):
return [x for t in terms for x in (t if isinstance(t, list) else [t])]
>>> concatenate([3, 4], 5, [], 7, [1])
[3, 4, 5, 7, 1]
>>> concatenate()
[]
您似乎想要的不仅仅是可变的,还有混合类型的签名
假设我们想定义一个concatenate_all(*args)
函数来连接抛出的所有参数
如果您同意concatenate\u all
的所有参数都是序列,那么我们可以将它们组成一个序列,并用concatenate
左折:
import itertools
# Pretend that concatenate_all is [[A]] -> [A]
def concatenate_all(*seqs):
all_seqs = itertools.chain(*seqs)
return reduce(lambda acc, x: concatenate(acc, x), all_seqs, EMPTY)
如果我们假设一些arg
是标量,一些是列表,那么我们可以将标量包装到列表中并使用相同的技巧
def concatenate_all(*scalars_or_seqs):
def to_list(x):
# TODO: should make it work with generators, too.
return x if isinstance(x, list) else [x]
# We use itertools to avoid creating intermediate lists.
all_items = itertools.chain(*scalars_or_seqs)
all_lists = itertools.imap(to_list, all_items)
return reduce(lambda acc, x: concatenate(acc, x), all_lists, EMPTY)
如果我们假设一些arg
也是需要展平的嵌套列表,那么您可以更新上面的代码来处理这个问题
我想警告您,不要让函数在参数方面过于聪明。最初可能会有过多的魔力,但在实践中变得很难推理,特别是在使用Python这样的高度动态语言时,几乎没有静态检查。最好将换行和展平推到调用方,并使其显式化。您似乎想要的不仅仅是可变的,还有混合类型的签名
假设我们想定义一个concatenate_all(*args)
函数来连接抛出的所有参数
如果您同意concatenate\u all
的所有参数都是序列,那么我们可以将它们组成一个序列,并用concatenate
左折:
import itertools
# Pretend that concatenate_all is [[A]] -> [A]
def concatenate_all(*seqs):
all_seqs = itertools.chain(*seqs)
return reduce(lambda acc, x: concatenate(acc, x), all_seqs, EMPTY)
如果我们假设一些arg
是标量,一些是列表,那么我们可以将标量包装到列表中并使用相同的技巧
def concatenate_all(*scalars_or_seqs):
def to_list(x):
# TODO: should make it work with generators, too.
return x if isinstance(x, list) else [x]
# We use itertools to avoid creating intermediate lists.
all_items = itertools.chain(*scalars_or_seqs)
all_lists = itertools.imap(to_list, all_items)
return reduce(lambda acc, x: concatenate(acc, x), all_lists, EMPTY)
如果我们假设一些arg
也是需要展平的嵌套列表,那么您可以更新上面的代码来处理这个问题
我想警告您,不要让函数在参数方面过于聪明。最初可能会有过多的魔力,但在实践中变得很难推理,特别是在使用Python这样的高度动态语言时,几乎没有静态检查。最好将换行和展平推到调用方并使其显式化。应该如何concatenate2([A,B],[C,D])
?(这种设计似乎在某种程度上混淆了类型,这只会让人头疼。)它应该返回[a,B,C,D](假设这些都不是列表)。所以你也要展平嵌套列表吗?是的,我刚刚意识到我上面的定义可能是错误的,因为它没有考虑嵌套列表。我真的希望,concatenate
的最终结果是一个不是列表的对象,或者一个这样的对象列表(因此没有嵌套列表)。concatenate2([a,B],[C,D])
应该怎么做?(这种设计似乎在某种程度上混淆了类型,这只会让人头疼。)它应该返回[a,B,C,D](假设这些都不是列表)。所以你也要展平嵌套列表吗?是的,我刚刚意识到我上面的定义可能是错误的,因为它没有考虑嵌套列表。我真的希望,concatenate
的最终结果是一个不是列表的对象,或者一个这样的对象的列表(因此没有嵌套列表)。只是为了确定,isinstance(t,list)是否与type(t)==list相同?它更自由一些,因为它也将