修改迭代器

时间:2010-06-10 09:04:20

标签: scala iterator

我有一个从集合( a )创建的迭代器( ite ):

var a = Set(1,2,3,4,5)
var ite = a.iterator

如果我删除了我的集合中的2个元素:

a -= 2

现在,如果我为所有元素移动迭代器,我将获得所有元素(包括2个元素)。没关系,但是......我怎么告诉迭代器删除2个元素?

5 个答案:

答案 0 :(得分:5)

问题是您的Set是不可变的(Scala默认为不可变集合),但由于a是变量而不是值,因此以下调用a -= 2实际上正在执行 a = a - 2。因此它不会改变集合,而是将新集合分配给a

相反,您可以使用可变集:var a = collection.mutable.Set(1,2,3,4,5)

scala> var a = collection.mutable.Set(1,2,3,4,5)
a: scala.collection.mutable.Set[Int] = Set(1, 4, 2, 5, 3)

scala>  val i = a.iterator
i: Iterator[Int] = non-empty iterator

scala> a -= 2
res0: scala.collection.mutable.Set[Int] = Set(1, 4, 5, 3)

scala> i.toList
res1: List[Int] = List(5, 3, 1, 4)

答案 1 :(得分:3)

您可以存储已删除的迭代器元素并跳过这些元素。

class DeletableIterator[X](it : Iterator[X]) extends Iterator[X]{
    val deleted = collection.mutable.Set[X]()
    var nextElement : Option[X] = getNext
    private def getNext : Option[X] = {
        if(it.hasNext) {
            val n = it.next
            if(deleted contains n) getNext else Some(n) 
        } else None
    }
    def next = {
        val r = nextElement.get
        nextElement = getNext
        r
    }
    def hasNext = nextElement.isDefined
    def -(x : X) = {
        deleted += x
        this
    }
}

implicit def iterator2DeltableIterator[X](i : Iterator[X]) = new DeletableIterator(i)

只要没有太多要删除的元素,这可能很有用。

var a = Set(1,2,3,4,5)
var ite = a.iterator
ite -= 2
ite -= 1
ite.toList

scala> ite.toList
res2: List[Int] = List(5, 3, 4)

答案 2 :(得分:1)

问题是你必须使用一个可变的Set才能工作。否则,将使用表达式a -= 2创建一个新集。以前生成的迭代器ite引用了第一个而不是新的迭代器。请参阅2个REPL会话的下一个记录(使用不可变和可变的Set

Welcome to Scala version 2.8.0.RC3 (Java HotSpot(TM) Client VM, Java 1.6.0_11).
Type in expressions to have them evaluated.
Type :help for more information.

scala> var a = Set(1,2,3,4,5)
a: scala.collection.immutable.Set[Int] = Set(4, 5, 1, 2, 3)

scala> var ite = a.iterator
ite: Iterator[Int] = non-empty iterator

scala> a -= 2

scala> a.toList
res1: List[Int] = List(5, 1, 3, 4)

scala> ite.toList
res2: List[Int] = List(5, 1, 2, 3, 4)

scala> ite.toList
res3: List[Int] = List()

----------------------------------

Welcome to Scala version 2.8.0.RC3 (Java HotSpot(TM) Client VM, Java 1.6.0_11).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.collection.mutable.Set
import scala.collection.mutable.Set

scala> var a = Set(1,2,3,4,5)
a: scala.collection.mutable.Set[Int] = Set(1, 4, 2, 5, 3)

scala> var ite = a.iterator
ite: Iterator[Int] = non-empty iterator

scala> a -= 2
res0: scala.collection.mutable.Set[Int] = Set(1, 4, 5, 3)

scala> ite.toList
res1: List[Int] = List(5, 3, 1, 4)

scala> ite.toList
res2: List[Int] = List()

答案 3 :(得分:1)

看起来你很困惑。 a -= 2不会删除元素2.如果可以,那么根据定义,a将是可变集,因为您正在更改它。 a -= 2创建一个看起来像旧的集合的全新集合,并将其分配给变量aite如何神奇地知道这已经发生了?

答案是,它不能 - 如果它已经开始运行了。实际上,就是使用不可变数据结构的整点。如果其他人现在认为这个世界看起来不同,那么你就不会感到困惑 - 你继续处理你看起来和以前一样的旧版本。

但是如果迭代器还没有已经被使用过,而你只想要一个方便的标签,意思是“获取一个迭代器”,你可以

var a = Set(1,2,3,4,5)
val ite = () => a.iterator
a -= 2
ite().foreach(println)  // Prints 5,1,3,4 or the like--no 2

这里,ite不是变量,它恰好是a.iterator的闭包。每次使用它时都会调用a.iterator,因此它始终是最新的(至少在最初阶段)。您还可以创建一个惰性迭代器,它实际上不会选择要使用的版本a,直到需要它为止:

var a = Set(1,2,3,4,5)
lazy val ite = a.iterator   // Not a.iterator now, but will be when we need it!
a -= 2
ite.foreach(println)   // Need it now--so we'll get 5,1,3,4

请注意,在这种情况下,如果调用一个采用迭代器的方法,则在传递给函数时将设置ite。以前的版本 - 它接受一个返回迭代器的函数 - 只会创建一次迭代器。

现在,您可能需要一个可以通过查看a的当前值来处理已删除条目的迭代器。您也可以这样做,但如果删除的结果返回None并且未删除的结果返回Some(x),则更容易实现:

var a = Set(1,2,3,4,5)
val ite = new Iterator[Option[Int]] {
  private val it = a.iterator
  def hasNext = it.hasNext
  def next = {
    val i = it.next
    if (a(i)) Some(i) else None
  }
}
a -= 2
ite.foreach(println)  // Some(5),Some(1),None,Some(3),Some(4)

(否则,您必须缓存值,因为此迭代器中的hasNext需要在next的迭代器中调用a,因此它可以检查该元素是否仍然存在。)

无论如何,有很多选择,但你需要决定做哪些明智的事情,这是你真正想要的。

答案 4 :(得分:0)

如果你绝对坚持这个狭隘的任务定义,你就会遇到问题。

重新定义问题的一种方法是迭代集合的元素,同时收集一个包含要删除元素的新集合。然后在迭代循环完成后从输入集合中删除这些元素。

另一种方法是使用partition(...)方法从输入创建两个新集合,谓词为true的那些集合以及谓词为false的集合。