web-dev-qa-db-fra.com

Paramètre de fonction Kotlin: Val ne peut pas être réaffecté

J'ai écrit Arbre rouge-noir à Kotlin. Fun insertFixup rétablit l'équilibre après l'insertion d'un nouvel élément ( z: Node? est le nouvel élément). L'algorithme d'équilibrage des arbres est tiré de ici (pages 2-3). Le problème est que Kotlin ne me permet pas de réaffecter z à z.parent et z.parent.parent. Je veux que z soit un pointeur . La question est de savoir comment faire comprendre à Kotlin ce que je veux de lui?

class Node(key: Int) {...}

class BinarySearchTree {
    var root: Node? = null

    fun insert(newNode: Node) {...}

    fun RotateLeft(x: Node?) {...}

    fun RotateRight(x: Node?) {...}

    fun insertFixup(z: Node?) {
        var y: Node?
        while (z?.parent?.color == "RED") {
            if (z?.parent == z?.parent?.parent?.left) {
                y = z?.parent?.parent?.right
                if (y?.color == "RED") {
                    z?.parent?.color = "BLACK"
                    y?.color = "BLACK"
                    z?.parent?.parent?.color = "RED"
                    z = z?.parent?.parent
                }
                if (z == z?.parent?.right) {
                    z = z?.parent
                    RotateLeft(z)
                    z?.parent?.color = "BLACK"
                    z?.parent?.parent?.color = "RED"
                    RotateRight(z?.parent?.parent)
                }
            } else {
                y = z?.parent?.parent?.left
                if (y?.color == "RED") {
                    z?.parent?.color = "BLACK"
                    y?.color = "BLACK"
                    z?.parent?.parent?.color = "RED"
                    z = z?.parent?.parent
                }
                if (z != z?.parent?.left) {
                    z = z?.parent
                    RotateLeft(z)
                    z?.parent?.color = "BLACK"
                    z?.parent?.parent?.color = "RED"
                    RotateRight(z?.parent?.parent)
                }
            }
        }
        root?.color = "BLACK"
    }
}

fun main(args: Array<String>) {
    val bst = BinarySearchTree()

    while (true) {
        var newNode = Node(readLine()!!.toInt())
        bst.insert(newNode)
        bst.insertFixup(newNode)
    }
}

UPD: Merci à tous! Toutes les réponses ont été utiles et j'ai trouvé la solution dans vos réponses.

10
Anton Ostrouhhov

Les paramètres de fonction dans Kotlin sont des val en lecture seule à l'intérieur de la fonction, donc z ici fera toujours référence à l'objet d'origine qui a été transmis.

Si vous devez modifier ce qu'il pointe pendant que votre fonction est en cours d'exécution, vous devrez en faire une copie locale au début de la fonction, puis vous pourrez en faire un var.

Par exemple, vous pouvez démarrer votre fonction comme ceci:

fun insertFixup(_z: Node?) {
    var z = _z
15
zsmb13

Les paramètres de la fonction Kotlin sont des valeurs en lecture seule et ne sont pas attribuables.

Vous pouvez cependant créer un objet ReadWriteProperty à passer à insertFixup pour obtenir/paramétrer newNode:

...
class BinarySearchTree {
...
    fun insertFixup(zProperty: ReadWriteProperty<Any?, Node?>) {
        var z by zProperty
...

fun main(args: Array<String>) {
    val bst = BinarySearchTree()

    var newNode: Node? = null
    val newNodeProperty = object : ReadWriteProperty<Any?, Node?> {
        override operator fun getValue(thisRef: Any?, property: KProperty<*>): Node? {
            return newNode
        }

        override operator fun setValue(thisRef: Any?, property: KProperty<*>,
                                       value: Node?) {
            newNode = value
        }
    }

    while (true) {
        newNode = Node(readLine()!!.toInt())
        bst.insert(newNode!!)
        bst.insertFixup(newNodeProperty)
    }
}

Et si vous souhaitez utiliser une propriété au lieu d'une variable, vous pouvez utiliser un référence de propriété pour obtenir/définir newNode à partir de insertFixup à la place:

...
class BinarySearchTree {
...
    fun insertFixup(zProperty: KMutableProperty0<Node?>) {
        var z by zProperty
...

var newNode: Node? = null

fun main(args: Array<String>) {
    val bst = BinarySearchTree()

    while (true) {
        newNode = Node(readLine()!!.toInt())
        bst.insert(newNode!!)
        bst.insertFixup(::newNode)
    }
}

// the following allow `KMutableProperty0` to be used as a read/write delegate
operator fun <T> KProperty0<T>.getValue(thisRef: Any?, property: KProperty<*>): T = get()
operator fun <T> KMutableProperty0<T>.setValue(thisRef: Any?, property: KProperty<*>, 
                                               value: T) = set(value)
5
mfulton26

J'ai également rencontré ce problème. Ce que j'ai fait, c'est créer une classe de données et passer la classe de données en tant que paramètre que je pourrais ensuite utiliser pour modifier ses propriétés.

data class SomeDataClass(
    val x: Int,
    val y: Int,
    val z: Int
)

fun someMethod(someDataClass: SomeDataClass) {
    someDataClass.z = 23 //whatever Int value you please
    // more computations...
    someDataClass.z = 67 // or whatever new value you need to assign.
}

fun parentMethod() {
    val someDataClass = SomeDataClass()
    someMethod(someDataClass)
    val newZValue = someDataClass.z // someDataClass holds modified data from above 
                                    // method
}
0
Bryan Neuberger