Explain Kotlin: forEach can also break and continue
Problems like this. That is, they want to use a forEach instead of a for loop, because it's fp and foreign (and I like it), the
But then they want to use break and continue, the control statements in a normal flow control statement.
It's pretty unfp, since there were originally filters that were used to accomplish this, as well as flapMap. bennyHuo is also talking about this approach in the article he posted.
filter very fp, but will lead to two traversals, which gives a very inefficient rush. Java8's Stream API will only traverse once.
But it generates lambda objects and is super complicated to implement (I haven't seen it, I'm not sure), whereas Kotlin's collection framework inlines the lambda.
How many fewer objects have been generated, and how can you be in the same boat as Hot Chicken Java?
Someone mentioned using a label return, for example:
fun main(ags: Array<String>) { (0..100).forEach { if (50 <= it) return@forEach println(it) } }
But after he experimented with it, he realized that it's only equivalent to continue, which means you can only jump out of the current loop, and then you'll still move on to the next round.
Reasoning this you can find out if you think about it. To make sense of it, let's implement a forEach ourselves.
fun Pair<Int, Int>.forEach(block: (Int) -> Unit) { for (i in first..second) (i) }
Then call it up:
Pair(1, 100).forEach(::println)
No problem old iron.
Then you'll realize that you're generating (second - first) calls to the block inside the function, and no matter how you RETURN, you'll just pop out of the block, the
It doesn't affect your ability to continue calling the block afterwards, which means that the for loop is unaffected by the behavior of the block.
It seems insoluble, so what to do?
Then let me save you.
fun main(ags: Array<String>) { run outside@ { (0..20).forEach inside@ { if (10 <= it) return@outside println(it) } } }
Run the result after compiling:
0 1 2 3 4 5 6 7 8 9 Process finished with exit code 0
Nah, jumped out.
Make the name of the LABEL halal, that's all:
run breaking@ { (0..20).forEach continuing@ { if (10 <= it) return@breaking println(it) } }
This is break above, and it runs as above.
Below this is continue, and the result of running it is the effect of continue. I've copied the println to make the effect more obvious.
Separately before and after the if so you can clearly see the effect.
run breaking@ { (0..20).forEach continuing@ { print(it) if (10 <= it) return@continuing println(it) } }
Run it:
00 11 22 33 44 55 66 77 88 99 1011121314151617181920 Process finished with exit code 0
And with only one iteration, it's very halal and seems more efficient.
How can I prove that there is only one iteration? I reversed the earlier code using jd-gui and the result:
public final class _5Kt { public static final void main(@NotNull String[] args) { (args, "args"); int $i$a$1$run; Iterable $receiver$iv = (Iterable)new IntRange(0, 20); int $i$f$forEach; for (Iterator localIterator = $receiver$(); ();) { int element$iv = ((IntIterator)localIterator).nextInt();int it = element$iv; int $i$a$1$forEach; (it); if (10 <= it) { break; } (it); } } }
It was indeed only once, and jd-gui directly decompiled my behavior to break. Convinced?
Thanks for reading, I hope this helps, and thanks for supporting this site!