Изменить итератор - PullRequest
       6

Изменить итератор

0 голосов
/ 10 июня 2010

У меня есть итератор ( ite ), созданный из набора ( a ):

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

Если я удаляю 2 элемента из моего набора:

a -= 2

Теперь, если я переместу итератор для всех элементов, я получу все элементы (включая 2 элемента).Это нормально, но ... Как я могу сказать итератору удалить 2 элемента?

Ответы [ 5 ]

5 голосов
/ 10 июня 2010

Проблема в том, что ваш 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)
3 голосов
/ 10 июня 2010

Вы можете сохранить удаленные элементы для итератора и пропустить их.

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)
1 голос
/ 10 июня 2010

Похоже, вы в замешательстве. a -= 2 не удаляет элемент 2. Если это возможно, тогда a будет, по определению, набором mutable , поскольку вы его изменяете. a -= 2 создает новый набор, который выглядит как старый, и присваивает его переменной a. Как ite должен волшебным образом знать, что это произошло?

Ответ таков: нет, если он уже запущен. То есть, на самом деле, весь смысл в использовании неизменных структур данных. Если кто-то решит, что мир теперь выглядит по-другому, вас не смущает - вы продолжаете работать над своей старой копией, которая выглядит так же, как и раньше.

Но если итератор еще не уже использовался, и вам просто нужна удобная метка, означающая "получить итератор 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 в этом итераторе нужно будет вызвать next в итераторе a, чтобы он мог проверить, существует ли этот элемент еще.)

В любом случае, есть много вариантов, но вам нужно решить, какой разумный поступок вы действительно хотите сделать.

1 голос
/ 10 июня 2010

Проблема в том, что вы должны использовать изменяемый набор для этого. В противном случае создается новый набор с выражением: a -= 2. И ранее сгенерированный итератор ite имеет ссылку на первый, а не новый. Смотрите следующую стенограмму двух сессий 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()
0 голосов
/ 10 июня 2010

Если вы абсолютно, безусловно, настаиваете на этом узком определении задачи, у вас есть проблема.

Один из способов переосмыслить проблему - это перебирать элементы коллекции, собирая новую коллекцию, которая содержитэлементы, которые будут удалены.Затем удалите эти элементы из входной коллекции после завершения цикла итерации.

Другой способ заключается в использовании метода partition(...) для создания двух новых коллекций из входных данных: те, для которых предикат является истинным, и те, для которыхэто ложь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...