I am seeing a problem with some Scala 2.7.7 code I’m working on, that should not happen if it the equivalent was written in Java. Loosely, the code goes creates a bunch of card players and assigns them to tables.
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
}
}
The PlayerRegistrar assigns a number of players between tables. First, it works out how many tables it will need to break up the players between and creates a List of them.
Then in the second part of the code, it works out which table a player should be assigned to, pulls that table from the list and registers a new player on that table.
The list of players on a table is a var, and is overwritten each time registerPlayer() is called. I have checked that this works correctly through a simple TestNG test:
@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)
}
}
I then test the table assignment:
@Test def testAssignPlayerToTables_1table() = {
val tables = PlayerRegistrar.assignPlayersToTables(10, 10)
assertEquals(tables.length, 1)
assertEquals(tables(0).players.length, 10)
}
The test fails with “expected:<10> but was:<0>”. I’ve been scratching my head, but can’t work out why registerPlayer() isn’t mutating the table in the list. Any help would be appreciated.
The reason is that in the
assignPlayersToTablesmethod, you are creating a newTableobject. You can confirm this by adding some debugging into the loop:Yielding something like:
Note how the memory address of the table is different for each call.
The reason for this behaviour is that a
Rangeis non-strict in Scala (until Scala 2.8, anyway). This means that the call to the range is not evaluated until it’s needed. So you think you’re getting back a list ofTableobjects, but actually you’re getting back a range which is evaluated (instantiating a newTableobject) each time you call it. Again, you can confirm this by adding some debugging:Which gives you:
To do what you want, add a
toListto the end: