Assignment not allow in while expression?

That’s exactly how I write my code, too. I call it the early-return convention. In this case it is early-break.

Explanation: reduce the number of nested structures (especially nested if), by introducing returns as early as possible.

Java 8 stream API could be useful
https://docs.oracle.com/javase/8/docs/api/java/io/BufferedReader.html#lines--

Use this kind of code.

val reader = BufferedReader(reader)
var line = 1

while (line != null) {
  line = reader.readLine()
  if(line != null)
    System.out.println(line)
  else
    break
}

Thank you so much
U helped me so much
I was trying for many hours then I found ur code
the clue with this
val line = reader.readLine()
inside the loop
my error was using
var line = reader.readLine()
outside the loop

this is bad practice… you just clutter namespace with garbage names ‘reader’ and ’ line ’

It’s a well known and easily readable construct from the days of C. Nothing unsafe about it all.

while(a=b++)

a perfectly good idiom.

It’s entirely correct. And why it’s a core part of just about every single language out there.

How about this.

val reader = BufferedReader(reader)
var line = reader.readLine()

while (line != null) {
  System.out.println(line)
  line = reader.readLine()
}

This does sound very sane to me: in most languages, things like break/if–else are straight statements, as
they can’t return a value, but in Kotlin, we have those keywords as expressions, even though they have no value.

Conversely, C et al allow assignments to be evaluated as expressions (as they have a natural ‘value’) but Kotlin does not

I don’t get it. Why this reversal ?

Don’t like it; it duplicates the reader.readLine() bit, which is a code smell for me, especially if that
line were doing something more complicated

I don’t like that at all; it uses break for a ‘normal’ exit from a function, something that will happen
eventually; I’d rather keep break for abnormal loop exiits

Thanks a lot… for this life saving code’s snippet…

How about this gives inline fun?


inline infix fun (() -> Unit).gives(crossinline test: () -> Boolean): Boolean {
    this()
    return test()
}

fun main(args: Array<String>) {
    var i = 0
    while({ i += 1 } gives { i < 10 }) {
    	println("Hello, world! $i")        
    }
}

That means you’re not restricted to the test being “is not null” as with the (admittedly useful) ?: break approach. Indeed, the action and test can both involve arbitrary operations. And it keeps the side-effect and the test “expression” cleanly separate, in a terse way.

Sadly https://youtrack.jetbrains.com/issue/KT-5837 means the receiver can’t be crossinlined.

Since this thread seems to be getting attention again, I want to include links to some helpful resources.
The primary use case discussed has been reading lines from a BufferedReader, which in Java is often solved using an assignment in a while loop.
You could use some of the previously suggested methods in order to read lines in a similar manner, but in Kotlin there are several built-in options worth considering:

Kotlin extensions to Reader
forEachLine(), readLines(), useLines() and more…

Kotlin extensions to BufferedReader
lineSequence()

A relevant StackOverflow question

2 Likes

Or this, just for fun. Maybe less efficient but avoids having to declare a variable outside the loop.


import java.io.BufferedReader
import java.io.StringReader

fun main() {
    val reader = BufferedReader(StringReader("Foo".split("").joinToString("\n")))
    for (line in { reader.readLine() } sequenceWhile { it != null }) {
        println(line)
    }
}

inline infix fun <T> (() -> T).sequenceWhile(crossinline test: (T) -> Boolean): Sequence<T> = sequence {
    while (true) {
        this@sequenceWhile().also { if (test(it)) { yield(it) } else return@sequence }
    }
}

BTW, just as a quick hint, you could also use a .also with the assignment and it works just fine. So for your example, it will be like this:

BufferedReader reader = new BufferedReader(reader);

String line = null;

while (reader.readLine().also{ line = it } != null) {

  System.out.println(line);

}

(This can also work with .apply, but IMO it looks nicer with the it instead of this

1 Like

Thanks a lot! it’s the best one.
When I read such creativity, I feel like a dumb ass :rofl:

It’s fun to read creative code, but it’s better to write boring code.

3 Likes

What’s creative today will be boring tomorrow.

2 Likes

This is a very long thread, which is started in 2013, and still kind of active, which alone shows that, this is not applicable only for a very small number of codebases. Apparently, this is a little bit of a headache which many developers noticed at a point.

I have been using Kotlin for 2.5 years and I find it a very fancy language. What I like most about it is being pragmatic while trying to tackle biggest challenges like null safety and immutability.

I read the whole thread and couldn’t find a neat solution. Either you can’t define the line only in the while loop scope, or you need to make it mutable, or you need to use a break which is also breaking the code flow! AFAIK, all of these are discouraged by Kotlin community.

The best one in the whole thread for me is something similar to @vitalyb 's suggestion. maybe this:

do {
  val line = reader.readLine()
         ?.also{ println(it) }
} while (line != null)
1 Like