49

I have written Red–black tree in Kotlin. Fun insertFixup restores balance after inserting new element (z: Node? is new element). Algorithm of tree balancing is taken from here (pages 2-3). The problem is that Kotlin does not allow me to reassign z to z.parent and z.parent.parent. I want z to be a pointer. The question is how to make Kotlin understand what I want from him?

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: Thanks to all! All the answers were helpful and I have found the solution in your replies.

hotkey
  • 140,743
  • 39
  • 371
  • 326
Anton Ostrouhhov
  • 591
  • 1
  • 4
  • 7
  • 2
    A minor note: I think you can improve the code greatly if you check if `z` is `null` or not only once in the beginning of `insertFixup`. Right now there are kinda too much `?` all over the place ;) – voddan Mar 01 '17 at 21:13
  • In Java, function parameters can be made readonly by adding `final` keyword before the parameter. Well Kotlin has that by default – abitcode Oct 30 '19 at 19:34

4 Answers4

66

Function parameters in Kotlin are basically read-only val's inside the function, so z here will always refer to the original object that was passed in.

If you need to modify what it points to while your function is running, you'll have to make a local copy of it at the beginning of the function, and then you can make that a var.

For example, you could start your function like this, which lets you reassign this local var later:

fun insertFixup(_z: Node?) {
    var z = _z
    // ...
    z = z.parent
    // ...
}
zsmb13
  • 85,752
  • 11
  • 221
  • 226
  • 22
    This does not answer the question on how to make `z` pointer-like. – mfulton26 Mar 01 '17 at 21:10
  • 1
    I didn't understand why you make a local copy of z and made it var. You cannot reassign to z, even if you make local copy of it like you said. That does not work. This answer should be wrong or correct me please – metis Mar 04 '20 at 12:04
  • The question was about reassigning the value of the function parameter, such as `z = z.parent`, inside the function. This isn't possible, but if you use a local `var` instead, you can reassign that local `var` as you like. – zsmb13 Mar 04 '20 at 17:22
  • "if you use a local var instead, you can reassign that local var as you like" ... so you still cant apply the change to the loop? I can't see how it's helpful. Another great kotlin tradeoff: take away the ability to do basic computer science so the poorer engineers dont fall over – Nick Cardoso Nov 29 '21 at 14:32
8

Kotlin function parameters are read-only values and are not assignable.

You can however create a ReadWriteProperty object to pass to insertFixup for getting/setting 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)
    }
}

And if you are willing to use a property instead of a variable then you can use a property reference for getting/setting newNode from insertFixup instead:

...
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)
mfulton26
  • 29,956
  • 6
  • 64
  • 88
1

I ran into this issue as well. What i did was create a data class and pass the data class as a parameter that i could then use to modify its properties.

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
}
Bryan Neuberger
  • 198
  • 2
  • 4
0

In my case, I was working on the content provider which manages the data for different application. I got a new kotlin file that inheritance properties from Content Provider like this:

class ProviderClass() : ContentProvider(){

}

ContentProvider() is an abstract class, so we need to implement all the members.

I was trying to reassigned value to sortOrder parameter, on that one I got these issues. What I did to solve is, //After implementing members

....
override fun query(
        uri: Uri,
        projection: Array<out String>?,
        selection: String?,
        selectionArgs: Array<out String>?,
        sortOrder: String?
    ): Cursor? {
var _sortOrder = sortOrder
if (sortOrder == null || sortOrder == ""){
            _sortOrder = NAME
        }
val cursor = qb.query(db, projection, selection, selectionArgs, null, null, _sortOrder) //_sorOrder newly created variable

I created a new variable called _sortOrder and put that newly created variable on the query method after two nulls on the second one where I created a new variable called cursor and set our newly created variable with the value that we want to set on it.

Taslim Oseni
  • 6,086
  • 10
  • 44
  • 69
sophin
  • 583
  • 5
  • 11