Why I can't apply value inside while loop?

Hey, I’d like to know why kotlin doesn’t support this code:

I’m using Android Studio 3.1.2 along with Kotlin 1.2.41
Thanks.

Because “assignments are not expression”, they are statements, like variable declarations or for/while loop.

The Kotlin team decided against it for a few reasons I think. In most cases it is just considered bad style and it also makes code harder to read. Yes your example is the one valid place where this kind of feature would be pretty handy, but they decided against it still, because only a small numbers of libraries would actually use something like this and it’s not worth adding an new feature to the language just for that.
Also there is a massive discussion about this here

1 Like

I recently encountered the similar problem. The solution was rather elegant:

do{
  val bytesRead = stream.read(buffer)
} while(byteRead != -1)

By the way, please do not use screenshots to present the code.

6 Likes

An alternative option is to use also

var bytesRead:Int =0
while(stream.read(buffer).also { bytesRead = it } >=0) {
  out.write(buffer, 0, bytesRead)
}

I agree it isn’t very pretty, perhaps the best way is to make it into some sort of inline function that encapsulates the ugliness of whatever code you end up using.

4 Likes

Or even maybe custom loop function can be useful

inline fun InputStream.whileReadBytes(array: ByteArray, block: (Int) -> Unit) {
    while (true) {
        val count = read(array)
        if (count < 0) break
        block(count)
    }
}

fun copy(input: InputStream, output: OutputStream) {
    val bytes = ByteArray(512)
    input.whileReadBytes(bytes) { count ->
        output.write(bytes, 0, count)
    }
}
1 Like

First off for the simple example of copying from an input stream to an OutputStream there is already the InputStream.copyTo extension method in the standard library.

But to address this code suggestion, I would reverse that because I always feel dirty creating local variables and always try to find ways to avoid them:

inline fun ByteArray.whileReadBytes(input: InputStream, block: ByteArray.(Int) -> Unit) {
    while (true) {
        val count = input.read(this)
        if (count < 0) break
        this.block(count)
     }
}

fun copy(input: InputStream, output: OutputStream) {
    ByteArray(512).whileReadBytes(input) { output.write(this, 0, it) }
}
1 Like

I think this is the best solution in terms of length, I will use it, thanks.

Kotlin stdlib already has a copyTo function:

input.copyTo(output, bufferSize = 512)

Which I said in the first sentence of my post. But I assumed that it was just a simple example and the problem the OP was trying to solve was something more complicated.

But the point of my post was focusing on the design of the previously suggested whileReadBytes method which can be used for other things than copying to an output stream.

in java
while (pos1 < len1 && (ch1 = str1.charAt(pos1++)) == ‘0’)
{
zeroes1++;
}
same i want to write in kotlin please help me

If I interpreted your code correctly you are counting the number of leading zeroes. In that case try:
zeroes1 = str1.takeWhile { it == ’0’ }.length

You should probably make a new post for your question instead of attaching it to an unrelated post that hasn’t been updated in a year.

There are two problems in your Java, for Kotlin:

  1. assignment is a statement in Kotlin and therefore cannot appear as a subexpression (because it’s not an expression at all)
  2. Kotlin strings don’t answer to .charAt()

So, minimally changing and adding to your code to make it work:

fun main() {
    val str1 = "00010"
    val len1 = str1.length
    var zeroes1 = 0
    var pos1 = 0
    while (pos1 < len1) {
        val ch1 = str1[pos1++]
        if (ch1 != '0') break

        zeroes1++;
    }
    println(zeroes1)
}

You can’t assign ch1 in the test, so you need to do that in the body, and then following tests that rely on ch1 need to happen in the body as well, but they can still fulfill their role with a conditional break. You can use .get(pos1++) instead of [pos1++]

Looks like IntelliJ’s Java->Kotlin conversion doesn’t fix assignments in tests like this. Pity.

1 Like

Slight micro-optimization on storage:

zeroes1 = str1.asSequence().takeWhile { it == '0' }.count()
1 Like