Разное поведение, когда тип объявления отличается (Set vs TreeSet) - PullRequest
3 голосов
/ 30 декабря 2010
    var set = TreeSet(5,4,3,2,1)
    println(set)

    val diffSet: TreeSet[Int] = set
    // if I change above code to val diffSet: Set[Int] = set
    // the result is unsorted set.

    for (i <- diffSet; x = i) {
        println(i)
    }
    println("-" * 20)
    // the above code translates to below and print the same result
    val temp = diffSet.map(i => (i, i))
    for ((i, x) <- temp) {
        println(i)
    }

У меня вопрос, если я определил метод, подобный этому:

def genSet:Set[Int] = {
  TreeSet(5, 4, 3, 2, 1)
}

и когда я хочу использовать цикл for с ним

for (i <- genSet; x = i + 1) {
  println(x)
}

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

for (i <- genSet) {
  val x = i + 1
  println(x)
}

Ответы [ 4 ]

12 голосов
/ 30 декабря 2010

Почему версия map оказывается несортированной

Метод map (вызываемый функцией, которую мы будем вызывать func) принимает неявный параметр CanBuildFrom, который учитывает тип коллекции, к которой вызывается map, в дополнение к тип, который func возвращает, чтобы выбрать подходящий тип возврата. Это используется, чтобы заставить Map.map[Int] или BitSet.map[String] делать правильные вещи (возвращать списки общего назначения), в то время как Map.map[(String,Int)] или BitSet.map[Int] также делает правильные вещи (возвращать Map и BitSet) соответственно.

CanBuildFrom выбирается во время компиляции, поэтому он должен выбираться на основе статического типа набора, для которого вы вызываете map (тип, который компилятор знает во время компиляции) , Статический тип set равен TreeSet, но статический тип diffset равен Set. Тип dynamic обоих (во время выполнения) равен TreeSet.

Когда вы вызываете map на set (TreeSet), компилятор выбирает immutable.this.SortedSet.canBuildFrom[Int](math.this.Ordering.Int) в качестве CanBuildFrom.

Когда вы вызываете map на diffset (Set), компилятор выбирает immutable.this.Set.canBuildFrom[Int] в качестве CanBuildFrom.

Почему версия for оказывается несортированной

Петля

for (i <- genSet; x = i + 1) {
  println(x)
}

десугаров в

genSet.map(((i) => {
              val x = i.$plus(1);
              scala.Tuple2(i, x)
            })).foreach(((x$1) => x$1: @scala.unchecked match {
              case scala.Tuple2((i @ _), (x @ _)) => println(x)
            }))

Версия desugared включает функцию map, которая будет использовать несортированный CanBuildFrom, как я объяснил выше.

С другой стороны, петля

for (i <- genSet) {
  val x = i + 1
  println(x)
}

десугаров в

genSet.foreach(((i) => {
              val x = i.$plus(1);
              println(x)
            }))

Который вообще не использует CanBuildFrom, поскольку новая коллекция не возвращается.

6 голосов
/ 30 декабря 2010

Set не гарантирует заказ. Даже если базовый класс равен TreeSet, если ожидаемый результат равен Set, вы потеряете порядок в первом преобразовании, которое вы делаете.

Если вы хотите заказать, не используйте Set. Я предлагаю, скажем, SortedSet.

2 голосов
/ 30 декабря 2010

Измените sig на genSet, чтобы получить SortedSet

def genSet:SortedSet[Int] = {
  TreeSet(5, 4, 3, 2, 1)
}

Это, вероятно, какая-то ошибка.Я бы ожидал, что ваш код тоже будет работать.

Я думаю, что map является виновником.Это приводит к тому же поведению:

for (i <- genSet.map(_ + 1)) { println(i) }

И for(i <- genSet; x = i + 1) равно for(x <- genSet.map({i => i + 1}))

0 голосов
/ 31 декабря 2010

Вы можете сделать:

scala> for (i <-genSet.view; x = i + 1) println(x)
2
3
4
5
6

Хотя, это тип уловки, когда вы смотрите на него через несколько месяцев, вы можете задаться вопросом, почему вы добавили .view ...

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