【问题标题】:Modify iterator修改迭代器
【发布时间】:2011-03-02 01:52:54
【问题描述】:

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

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

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

a -= 2

现在,如果我移动所有元素的迭代器,我会得到所有元素(包括 2 个元素)。没关系,但是...我如何告诉迭代器删除 2 个元素?

【问题讨论】:

    标签: scala iterator


    【解决方案1】:

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

    答案是,如果它已经开始运行,它就不能。实际上,这就是使用不可变数据结构的全部意义。如果其他人认为世界现在看起来不同了,您也不会陷入混乱 - 您可以继续处理看起来与以往一样的旧副本。

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

    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 需要在 a 的迭代器中调用 next,以便检查该元素是否仍然存在。)

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

    【讨论】:

      【解决方案2】:

      如果你绝对、肯定地坚持对任务的这种狭隘定义,那你就有问题了。

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

      另一种方法是使用partition(...) 方法从输入创建两个新集合,一个谓词为真,一个谓词为假。

      【讨论】:

        【解决方案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)
        

        【讨论】:

        • 当下一个元素被移除元素时,迭代器返回该元素
        【解决方案4】:

        问题是你的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)
        

        【讨论】:

        • 我需要使用不可变集。我需要告诉迭代器删除元素所必需的。
        • 啊,我明白了。但是如果它是一个不可变的集合,你期望迭代器如何改变它呢?但即使对于可变集合,Scala 迭代器也不能改变它们的底层集合。看到这个问题:stackoverflow.com/questions/2803085/…
        • 也许您可以告诉我们更多您想要解决的问题,以便我们找到不涉及变异迭代器的解决方案?
        【解决方案5】:

        问题是你必须使用一个可变的 Set 才能工作。否则,将使用以下表达式创建一个新 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()
        

        【讨论】:

        • 我需要使用不可变集。我需要告诉迭代器删除元素所必需的。
        • 创建的迭代器引用了之前创建的 Set(1,2,3,4,5)。正如 Mirko Stocker 所说,a -= 2 实际上是对变量a 的重新赋值。但是迭代器ite 不知道这个变化,因为创建了一个新的Set(在不可变的情况下)。我认为使用不可变集合是不可能实现这一点的。
        • 是的,但我想告诉迭代器删除它。 ite.remove(2)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-03-26
        • 2013-05-06
        • 2011-01-16
        • 1970-01-01
        • 1970-01-01
        • 2017-08-23
        • 2021-02-15
        相关资源
        最近更新 更多