Объекты Scala не меняют свое внутреннее состояние - PullRequest
3 голосов
/ 31 января 2010

Я вижу проблему с некоторым кодом Scala 2.7.7, над которым я работаю, этого не должно произойти, если его эквивалент был написан на Java. Скорее всего, код создает группу игроков в карты и распределяет их по столам.

class Player(val playerNumber : Int)

class Table (val tableNumber : Int) {
    var players : List[Player]  = List()

    def registerPlayer(player : Player) {
        println("Registering player " + player.playerNumber + " on table " + tableNumber)
        players = player :: players
    }
}

object PlayerRegistrar  {
    def assignPlayersToTables(playSamplesToExecute : Int, playersPerTable:Int) = {
        val numTables = playSamplesToExecute / playersPerTable
        val tables = (1 to numTables).map(new Table(_))
        assert(tables.size == numTables)

        (0 until playSamplesToExecute).foreach {playSample =>
            val tableNumber : Int = playSample % numTables
            tables(tableNumber).registerPlayer(new Player(playSample))
        }
        tables
    }
}

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

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

Список игроков на столе является var и перезаписывается при каждом вызове registerPlayer (). Я проверил, что это работает правильно, с помощью простого теста TestNG:

@Test def testRegisterPlayer_multiplePlayers() {
    val table = new Table(1)
    (1 to 10).foreach { playerNumber =>
        val player = new Player(playerNumber)
        table.registerPlayer(player)
        assert(table.players.contains(player))
        assert(table.players.length == playerNumber)
    }
}

Затем я проверяю присвоение таблицы:

  @Test def testAssignPlayerToTables_1table() = {
    val tables = PlayerRegistrar.assignPlayersToTables(10, 10)
    assertEquals(tables.length, 1)
    assertEquals(tables(0).players.length, 10)
}

Тест не пройден с "ожидаемым: <10>, но было: <0>". Я почесал голову, но не могу понять, почему registerPlayer () не изменяет таблицу в списке. Любая помощь будет оценена.

Ответы [ 2 ]

4 голосов
/ 01 февраля 2010

Причина в том, что в методе assignPlayersToTables вы создаете новый объект Table. Вы можете подтвердить это, добавив отладку в цикл:

val tableNumber : Int = playSample % numTables
println(tables(tableNumber))
tables(tableNumber).registerPlayer(new Player(playSample))

Дает что-то вроде:

Main$$anon$1$Table@5c73a7ab
Registering player 0 on table 1
Main$$anon$1$Table@21f8c6df
Registering player 1 on table 1
Main$$anon$1$Table@53c86be5
Registering player 2 on table 1

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

Причина такого поведения в том, что Range не является строгим в Scala (во всяком случае, до Scala 2.8). Это означает, что вызов диапазона не оценивается, пока он не понадобится. Таким образом, вы думаете, что получаете список Table объектов, но на самом деле вы получаете диапазон, который оценивается (создание нового Table объекта) при каждом его вызове. Опять же, вы можете подтвердить это, добавив отладку:

val tables = (1 to numTables).map(new Table(_))
println(tables)

Что дает вам:

RangeM(Main$$anon$1$Table@5492bbba)

Чтобы сделать то, что вы хотите, добавьте toList в конце:

val tables = (1 to numTables).map(new Table(_)).toList
2 голосов
/ 01 февраля 2010
val tables = (1 to numTables).map(new Table(_))

Эта строка, кажется, вызывает все проблемы - отображение по 1 to n дает вам RandomAccessSeq.Projection, и, честно говоря, я не знаю, как именно они работают, но чуть меньше Умная техника инициализации делает свое дело.

var tables: Array[Table] = new Array(numTables)
for (i <- 0 to numTables) tables(i) = new Table(i)

Используя первый метод инициализации, я не смог изменить объекты (как и вы), но с помощью простого массива все работает.

...