2015-12-21 39 views
2

在很多场合,我们需要像flattencompact阵列上执行两个或两个以上不同的操作。扁平化和紧凑阵列更有效地

some_array.flatten.compact 

我这里关注的是,它会循环阵列上两次。有没有更有效的方法来做到这一点?

+2

当然,你自己动手做。 –

+4

是的,从自己的循环到使用C++而不是Ruby,有许多更有效的方法。问题是:你为什么在意?这实际上是一种瓶颈吗? – meagar

+1

你可以手动循环或获得一个懒惰的枚举器(假设你想使用的所有方法都在'Enumerable'中)。 – ndn

回答

4

我其实觉得这是一个很好的问题。但首先,为什么每个人都不太在意呢?下面是flattenflatten.compact相比性能:

flatten performance

Here's the code I used to generate this chart, and one that includes memory.

希望现在你明白为什么大多数人会担心:这只是你在撰写flatten添加另一常数因子用compact,也许是有价值的,至少从理论上说:我们怎样才能剃除时间和空间这个中间结构的?再次,渐近地不是超级有价值的,但好奇想想。

据我所知,你不能利用的flatten做到这一点:

查看源码之前,我希望flatten可能需要一个块,像这样:

[[3, [3, 3, 3]], [3, [3, 3, 3]], [3, [3, 3, 3]], nil].flatten {|e| e unless e.nil? } 

虽然没有骰子。我们将此作为回报:

[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, nil] 

这很奇怪,因为它基本上把块作为无操作来扔掉。但它对source有意义。在Ruby的核心中使用的C方法flatten未被参数化以获取块。

在Ruby源代码的程序读取有点怪我(我不是一个C程序员),但它基本上做类似深度优先搜索。它使用一个堆栈,它将每一个新的嵌套数组添加到它遇到的进程中。 (当没有剩下的时候它终止。)我没有正式计算这个,但它让我猜测复杂性与DFS一致。

因此源代码可以一直这样写这样会被允许额外的设置,如果一个块中传递工作。但是,如果没有,你就坚持与(小)的性能损失!

+0

顺便说一句,如果你真的想要能够做到这一点,我写了一个C扩展,增加了一个新的函数'collapse':https://github.com/mooreniemi/array_collapse –

1

它是不一样的阵列上进行迭代两次。 flatten通常会创建一个与原始结构完全不同的结构。因此,第一次和第二次迭代不会迭代相同的元素。所以,自然而然地,你不能那样做。

+1

true,但它仅仅意味着第二次循环会变得更小,它仍然会更有效率地做到这一点一旦。 –

0

如果阵列是一个层深,则该阵列可在一组合并。

require 'set' 
s = Set.new 
Ar.each{|a| s.merge(a)} 
+0

您正在依赖该集合不改变元素的顺序。 – ndn