2015-12-21 58 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