2013-12-23 44 views
1

假設我想要生成Set的子集的所有組合。由於subset返回iterator我不想將其轉換爲嚴格的東西。迭代器覆蓋for循環中的相同集合

def gen(A: Set[Int]) = { 
    val it0 = A.subsets 
    val it1 = A.subsets 
    for(a <- it0; b <- it1) yield (a,b) 
} 

但它不是我想要的。例如gen(Set(1,2,3)).foreach(println)返回:

(Set(),Set()) 
(Set(),Set(1)) 
(Set(),Set(2)) 
(Set(),Set(3)) 
(Set(),Set(1, 2)) 
(Set(),Set(1, 3)) 
(Set(),Set(2, 3)) 
(Set(),Set(1, 2, 3)) 

似乎只有第二個迭代器遍歷所有子集。爲什麼它的行爲如此並且有避免這種情況的好方法?

回答

4

請注意,it0it1Iterator s。不能使用迭代器這樣的:

val it0 = Iterator(1, 2) 
val it1 = Iterator(1, 2) 

(for { a <- it0; b <- it1 } yield (a, b)).toList 
// List[(Int, Int)] = List((1,1), (1,2)) 

這裏的原因是,你不能再疊代IteratorIterator是可變的。對於it0的第一個元素,您已重複使用it1,因此it1對於it0的下一個元素是空的。

您應該對第一個迭代器的每一個元素重新創建第二個迭代:

def gen(A: Set[Int]) = 
    for{ 
    a <- A.subsets 
    b <- A.subsets 
    } yield (a,b) 

或者convet Iterator到不可變集合:

def gen(A: Set[Int]) = { 
    val it = A.subsets.toSeq 
    for(a <- it; b <- it) yield (a,b) 
} 
+0

是啊,THX。缺陷在我的想法。我想避免將其轉換爲不可變的集合。 – Kigyo

+0

但是現在我測試了它,我的算法(也有其他一些東西)比我用不可變集合的初始解決方案慢3倍。太糟糕了。只是想節省一些空間。 – Kigyo

+0

@Kigyo:[子集的實現](https://github.com/scala/scala/blob/v2.10.3/src/library/scala/collection/SetLike.scala#L175)很貴,包括'N'調用昂貴的'toIndexedSeq'操作(1代表'len'從'0'到'Set#size')。 – senia