Frage

Ich sehe ein Problem mit einigen Scala 2.7.7 Code arbeite ich an, das nicht passieren sollte, wenn es das Äquivalent in Java geschrieben wurde. Lose, geht der Code eine Reihe von Kartenspieler schafft und ordnet sie Tabellen.

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
    }
}

Die PlayerRegistrar weist eine Reihe von Spielern zwischen den Tischen. Erstens, es funktioniert, wie viele Tabellen es müssen die Spieler zwischen aufzubrechen und erstellt eine Liste von ihnen.

Dann im zweiten Teil des Codes, es funktioniert, welche Tabelle ein Spieler zugewiesen werden soll, zieht die Tabelle aus der Liste und Registern einen neuen Spieler auf dieser Tabelle.

Die Liste der Spieler auf einem Tisch ist ein var, und wird überschrieben jedes Mal registerPlayer () aufgerufen wird. Ich habe überprüft, dass diese korrekt durch einen einfachen TestNG Test funktioniert:

@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)
    }
}

ich die Tischzuweisung dann testen:

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

Der Test mit fails "erwartet: <10> aber war: <0>". Ich habe meinen Kopf kratzen worden, kann aber nicht herausfinden, warum registerPlayer () nicht die Tabelle in der Liste mutiert. Jede mögliche Hilfe würde geschätzt.

War es hilfreich?

Lösung

Der Grund dafür ist, dass in der assignPlayersToTables Methode, ein neues Table Objekt erstellen. Sie können dies bestätigen, indem einige Debug in die Schleife fügt hinzu:

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

Nachgeben so etwas wie:

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

Beachten Sie, wie die Speicheradresse der Tabelle für jeden Anruf unterschiedlich ist.

Der Grund für dieses Verhalten ist, dass ein Range ist nicht streng in Scala (bis Scala 2.8, sowieso). Dies bedeutet, dass der Anruf auf den Bereich nicht ausgewertet wird, bis es gebraucht wird. Sie glauben also, Sie bekommen eine Liste von Table Objekte zurück, aber eigentlich, Sie bekommen einen Bereich zurück, die (instanziieren ein neues Objekt Table) ausgewertet wird jedes Mal, wenn Sie es nennen. Auch hier können Sie dies bestätigen, indem einige Debug hinzufügen:

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

Welche gibt Ihnen:

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

tun, was Sie wollen, fügen Sie ein toList bis zum Ende:

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

Andere Tipps

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

scheint diese Linie alle die Mühe zu verursachen - Mapping über 1 to n gibt Ihnen eine

scroll top