2011-10-01 68 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