2011-10-01 74 views
12

我對方法take在特徵Iterator中的行爲感到困惑。看起來它不會消耗物品。下面是一個例子:從scala中消耗項目迭代器

scala> Iterator(1,2,3) 
res0: Iterator[Int] = non-empty iterator 

scala> res0 take 2 toArray 
res1: Array[Int] = Array(1, 2) 

scala> res0.next 
res2: Int = 1 

顯然步驟2消耗兩個項目,但在步驟3中的Iterator仍處於第一項。看看實現,我看不到任何複製或緩衝,只是一個新的Iterator委託給底層。這怎麼可能?我怎樣才能真正消費n項目?

回答

10

所討論的迭代器在IndexedSeqLike#Elementssource)中定義。 A ticket was recently filed關於take跨不同迭代器實現的不一致行爲。

要真正消耗N件物品,請致電Iterator#next N次。

您可能需要考慮使用Stream,這是一種懶惰(如Iterator),但也是不可變的(與Iterator不同)。

scala> val s = Stream(1, 2, 3) 
s: scala.collection.immutable.Stream[Int] = Stream(1, ?) 

scala> s.take(2).toList 
res43: List[Int] = List(1, 2) 

scala> s.take(2).toList 
res44: List[Int] = List(1, 2) 

scala> s.drop(2).toList 
res45: List[Int] = List(3) 

scala> {val (s1, s2) = s.splitAt(2); (s1.toList, s2.toList)} 
res46: (List[Int], List[Int]) = (List(1, 2),List(3)) 
+0

謝謝retronym,我明白了這一點。是什麼讓我放棄了'Stream'就是缺乏像「當前項目指針」這樣的東西(順便說一句,也許是由於Java的影響力,可能是所謂的東西)。現在我看到,我可以通過一系列「drop」並查看「head」來獲得「當前指針」。我會再試一次。 – jglatre

1

你想要消耗的物品,drop他們。請注意,在Iterator上調用的大多數方法都會使Iterator無用,以便進一步使用 - 在行爲未定義且可能更改的意義上無用。

+0

'drop'實際上受到與'take'相同的不一致性。 '{val i = Iterator(1,2,3); i.drop(1); i.next}'返回'1',而不是'2'。 – retronym

+1

@retronym一旦你在'i'上調用'drop',就不能可靠地使用'i'了。相反,你必須使用返回的「drop」。 –

+0

好點。 '{val i = Iterator(1,2,3); i.drop(1).next}給出2.除next()和hasNext()之外的所有內容(可能)使原始迭代器失效;儘管這些方法可能會返回* new *,valid,iterator。我期待着您的文件貢獻來澄清這一點。 – retronym

2

謝謝你們。

這是我的解決方案,從Iterator消耗物品的束:

implicit def consumable(i: Iterator[_]) = new { 
    def next(n: Int) = { 
     (for (_ <- 1 to n) yield i.next()).iterator 
    } 
    def skip(n: Int) { 
     (1 to n).foreach(_ => i.next()) 
    } 
    } 

任何意見會受到歡迎。

+0

爲了與返回類型一致,定義應該是: 隱式def consumable [T](i:Iterator [T]),否則返回Iterator [Any] – Miquel