我有一个从集合( a )创建的迭代器( ite ):
var a = Set(1,2,3,4,5)
var ite = a.iterator
如果我删除了我的集合中的2个元素:
a -= 2
现在,如果我为所有元素移动迭代器,我将获得所有元素(包括2个元素)。没关系,但是......我怎么告诉迭代器删除2个元素?
答案 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
创建一个看起来像旧的集合的全新集合,并将其分配给变量a
。 ite
如何神奇地知道这已经发生了?
答案是,它不能 - 如果它已经开始运行了。实际上,就是使用不可变数据结构的整点。如果其他人现在认为这个世界看起来不同,那么你就不会感到困惑 - 你继续处理你看起来和以前一样的旧版本。
但是如果迭代器还没有已经被使用过,而你只想要一个方便的标签,意思是“获取一个迭代器”,你可以
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的集合。