for-loopkotlin

Is it possible to write a for loop in Kotlin where I can change the index to any value I want during the iteration?


Is it possible to write a for loop in Kotlin where I can change the index to any value I want during the iteration?

I know I can write a for loop like that:

for (i in 1..3) {
    println(i)
}
for (i in 6 downTo 0 step 2) {
    println(i)
}

But I can't do what I want. I would like to write a for loop like in java

for (int i = 0; i < n; i++) {
    ...
    if (condition) i = <any valid index>
}

but be able to change the value of i to any value I want. There are many times when it is necessary.

My current use case is that I'm checking if tag is valid. I use a for loop to iterate through the text. When the current character is equal to "<", I find the index of the next ">" and check if the text between them is a valid tag name. If so, I set the value of i to the index of ">". I can't find a way of doing that with a for loop in Kotlin, only with a while loop. If I use a while loop, I need to declare the index outside and handle the increment inside the loop, which is a bit annoying and error prone.


Solution

  • As you've found, the index of a …in… for loop is read-only in Kotlin; you can't change it within the loop.

    There are two main approaches.  First, in some cases you may be able to include the jumps in the iteration itself — perhaps iterating over the result of some process, instead of over a simple range.  This can make the loop clearer and more concise; but it only applies in some specific cases.

    Otherwise, I'm afraid the general case is simply to use a while loop instead, as you suggest:

    var i = 1
    while (i <= 3) {
        println(i)
        ++i
    }
    
    i = 6
    while (i >= 0) {
        println(i)
        i -= 2
    }
    

    As you say, this of course more awkward: the initial value, increment/decrement, and final value are now in three different places, and so the loop is harder to read.  And the loop variable is no longer restricted to the scope of the loop itself, which pollutes the namespace of the enclosing function/block and risks confusion later (especially if the variable is reused, as above).

    But it's much more general: the loop variable can be adjusted by any amount each time through the loop, even in a way not known before-hand.

    Unfortunately, there's no easy way to wrap this into a general higher-order function (which is how Kotlin allows many other language constructs to be faked^H^H^H^H^Himplemented).  This is for the same reason: lambda parameters (whether called it, or named explicitly) are read-only.

    And to anticipate your next question: I don't think there's an official word on why for indexes, like function parameters, are read-only, but I suspect it's because having them mutable can lead to confusion and other errors.  (If your Java example was long and complex, it might be easy to miss the update to i inside the loop, and assume from the top of the loop that it was only ever incremented by 1 each time.  Or there might be the risk of updating the loop counter accidentally, making a typo for a different variable.)