return#forEach does not seem to exit forEach{} - kotlin

The count is 4 at the end of the code below. I expected 0. Why is it 4? How can I get 0?
var count = 0;
"hello".forEach {
if(it == 'h')
{
println("Exiting the forEach loop. Count is $count");
return#forEach;
}
count++;
}
println("count is $count");
Output:
Exiting the forEach loop. Count is 0
count is 4

return#forEach does not exit forEach() itself, but the lambda passed to it ("body" of forEach()). Note that this lambda is executed several times - once per each item. By returning from it you actually skip only a single item, so this is similar to continue, not to break.
To workaround this you can create a label in the outer scope and return to it:
var count = 0;
run loop# {
"hello".forEach {
if(it == 'h')
{
println("Exiting the forEach loop. Count is $count");
return#loop;
}
count++;
}
}
This is described here: https://kotlinlang.org/docs/returns.html#return-at-labels
Note that the use of local returns in previous three examples is similar to the use of continue in regular loops. There is no direct equivalent for break, but it can be simulated by adding another nesting lambda and non-locally returning from it

It is 4 because the forEach call the lambda passed to it for each character in the string, so the return#forEach in your code return for the first element. You can use a for loop and use break to obtain 0.

return#forEach returns from the lambda function. But the forEach function is a higher-order function that calls the lambda repeatedly for each item in the iterator. So when you return from the lambda, you are only returning for that single item in the iterator. It is analogous to using continue in a traditional for loop.
If you want to exit iteration in a higher-order function completely, you have to use labels. And as I type this, I see another answer already shows how to do that, but I'll leave this in case the different explanation helps.

If your objective is to count the number of characters before 'h', you could do something like this:
val numCharsBeforeH = "hello".takeWhile { it != 'h' }.length
From your comment to Tenfour04's answer:
This is not very convenient. Why didn't the makers of Kotlin create a "break" equivalent?
Here is a quote of the "Loops" section of the Coding conventions:
Prefer using higher-order functions (filter, map etc.) to loops.
Exception: forEach (prefer using a regular for loop instead, unless
the receiver of forEach is nullable or forEach is used as part of a
longer call chain).
When making a choice between a complex expression using multiple
higher-order functions and a loop, understand the cost of the
operations being performed in each case and keep performance
considerations in mind.
Indeed, using a regular for loop with break does what you expect:
var count = 0;
for (char in "hello") {
if (char == 'h') {
println("Breaking the loop. Count is $count")
break
}
count++
}
println("count is $count")
Output:
Breaking the loop. Count is 0
count is 0
Except for very simple operations, there are probably better ways to do what you need than using forEach.

Related

Run a regex on a Supply or other stream-like sequence?

Suppose I have a Supply, Channel, IO::Handle, or similar stream-like source of text, and I want to scan it for substrings matching a regex. I can't be sure that matching substrings do not cross chunk boundaries. The total length is potentially infinite and cannot be slurped into memory.
One way this would be possible is if I could instantiate a regex matching engine and feed it chunks of text while it maintains its state. But I don't see any way to do that -- I only see methods to run the match engine to completion.
Is this possible?
After some more searching, I may have answered my own question. Specifically, it seems Seq.comb is capable of combining chunks and lazily processing them:
my $c = supply {
whenever Supply.interval(1.0) -> $v {
my $letter = do if ($v mod 2 == 0) { "a" } else { "b" };
my $chunk = $letter x ($v + 1);
say "Pushing {$chunk}";
emit($chunk);
}
};
my $c2 = $c.comb(/a+b+/);
react {
whenever $c2 -> $v {
say "Got {$v}";
}
}
See also the concurrency features used to construct this example.

Kotlin - How to apply modulo operation on each element of a matrix?

I find Lambda in Kotlin to be very confusing and on the top of it is "it".
There are two things I know about "it" and i.e.
If your Lambda has their own argument, you can replace its name with "it".
"It" is an automatically generated name for your Lambda, if it has
only one argument, and you don't specify a different argument name.
Still I don't understand what actually passes as "it".
For E.g. I wanted to apply modulo function on each element of a 3x3 matrix.
fun main(){
var result = Array(3) {
IntArray(3) { 3;2;4;6;7;9;12;11;23 }
}
result = Array(3){ IntArray(3) {it%2} }
println(result.joinToString("\n") { it.joinToString(" ") })
}
Here I assumed that "it" takes each element of the matrix which is clearly not the case as my output was:
0 1 0
0 1 0
0 1 0
So can you please explain me how "it" works, what is happening here? and what would be the correct way to implement this program?
Your line
result = Array(3){ IntArray(3) {it%2} }
isn't doing anything to the original Array that result is pointing at. You are creating a brand new group of array objects by calling the Array and IntArray constructors.
The lambda that you pass to the IntArray constructor has an input parameter that represents the array index, and the return value of your lambda is what will be put into the array at that index. So in this case it is the array index, and your lambda is returning 0 and 1 for even and odd indices respectively.
You are also instantiating your array incorrectly to begin with. Your lambda that you pass to that IntArray constructor is throwing away a bunch of pointless Int values and then returning 23 for each item. So you've created a 3x3 matrix that is completely filled with the number 23.
The correct syntax for creating an array with explicit values is to use arrayOf or intArrayOf.
val result = arrayOf(
intArrayOf(3, 2, 4),
intArrayOf(6, 7, 9),
intArrayOf(12, 11, 23)
)
To modify all the values of an array, you typically iterate the traditional way, not with a lambda:
for (innerArray in result) {
for (i in innerArray.indices)
innerArray[i] = innerArray[i] % 2
}
You were probably thinking of the map function, which lets you pass a lambda and returns a new List with the lambda function applied to every element of the input collection. Or when working with collections other than arrays, you can use forEach or onEach to iterate them without modifying them.

How to properly iterate over arrays in kotlin

I am currently learning kotlin and therefore following the kotlin track on exercism. The following exercise required me to calculate the Hamming difference between two Strings (so basically just counting the number of differences).
I got to the solution with the following code:
object Hamming {
fun compute(dnaOne: String, dnaTwo: String): Int {
if (dnaOne.length != dnaTwo.length) throw IllegalArgumentException("left and right strands must be of equal length.")
var counter = 0
for ((index, letter) in dnaOne.toCharArray().withIndex()) {
if (letter != dnaTwo.toCharArray()[index]) {
counter++
}
}
return counter
}
}
however, in the beginning I tried to do dnaOne.split("").withIndex() instead of dnaOne.toCharArray().withIndex() which did not work, it would literally stop after the first iteration and the following example
Hamming.compute("GGACGGATTCTG", "AGGACGGATTCT") would return 1 instead of the correct integer 9 (which only gets returned when using toCharArray)
I would appreciate any explanation
I was able to simplify this by using the built-in CharSequence.zip function because StringimplementsCharSequence` in Kotlin.
According to the documentation for zip:
Returns a list of pairs built from the characters of this and the [other] char sequences with the same index
The returned list has length of the shortest char sequence.
Which means we will get a List<Pair<Char,Char>> back (a list of pairs of letters in the same positions). Now that we have this, we can use Iterable.count to determine how many of them are different.
I implemented this as an extension function on String rather than in an object:
fun String.hamming(other: String): Int =
if(this.length != other.length) {
throw IllegalArgumentException("String lengths must match")
} else {
this.zip(other).count { it.first != it.second }
}
This also becomes a single expression now.
And to call this:
val ham = "GGACGGATTCTG".hamming("AGGACGGATTCT")
println("Hamming distance: $ham")

Counter as variable in for-in-loops

When normally using a for-in-loop, the counter (in this case number) is a constant in each iteration:
for number in 1...10 {
// do something
}
This means I cannot change number in the loop:
for number in 1...10 {
if number == 5 {
++number
}
}
// doesn't compile, since the prefix operator '++' can't be performed on the constant 'number'
Is there a way to declare number as a variable, without declaring it before the loop, or using a normal for-loop (with initialization, condition and increment)?
To understand why i can’t be mutable involves knowing what for…in is shorthand for. for i in 0..<10 is expanded by the compiler to the following:
var g = (0..<10).generate()
while let i = g.next() {
// use i
}
Every time around the loop, i is a freshly declared variable, the value of unwrapping the next result from calling next on the generator.
Now, that while can be written like this:
while var i = g.next() {
// here you _can_ increment i:
if i == 5 { ++i }
}
but of course, it wouldn’t help – g.next() is still going to generate a 5 next time around the loop. The increment in the body was pointless.
Presumably for this reason, for…in doesn’t support the same var syntax for declaring it’s loop counter – it would be very confusing if you didn’t realize how it worked.
(unlike with where, where you can see what is going on – the var functionality is occasionally useful, similarly to how func f(var i) can be).
If what you want is to skip certain iterations of the loop, your better bet (without resorting to C-style for or while) is to use a generator that skips the relevant values:
// iterate over every other integer
for i in 0.stride(to: 10, by: 2) { print(i) }
// skip a specific number
for i in (0..<10).filter({ $0 != 5 }) { print(i) }
let a = ["one","two","three","four"]
// ok so this one’s a bit convoluted...
let everyOther = a.enumerate().filter { $0.0 % 2 == 0 }.map { $0.1 }.lazy
for s in everyOther {
print(s)
}
The answer is "no", and that's a good thing. Otherwise, a grossly confusing behavior like this would be possible:
for number in 1...10 {
if number == 5 {
// This does not work
number = 5000
}
println(number)
}
Imagine the confusion of someone looking at the number 5000 in the output of a loop that is supposedly bound to a range of 1 though 10, inclusive.
Moreover, what would Swift pick as the next value of 5000? Should it stop? Should it continue to the next number in the range before the assignment? Should it throw an exception on out-of-range assignment? All three choices have some validity to them, so there is no clear winner.
To avoid situations like that, Swift designers made loop variables in range loops immutable.
Update Swift 5
for var i in 0...10 {
print(i)
i+=1
}

Loop That Doesn't End Until Break

I'm trying to create a loop than continues to take input until the input gives the command to break the loop. What I'm doing now looks a little like this:
int start = 1;
while (start == 1) {
//Program statement
}
However, I feel as though there is an easier, more effective way to create a loop that repeats until the user gives the command to stop it. Does something like that exist?
A common idiom to express a "forever" loop in C and other C-like languages, including Objective-C, is to use an empty for:
for(;;) {
// statements
}
You should do it like this:
while(true)
{
if( exit_condition)
{
break;
}
}
do{
userInput = readUserInput()
}while(userInput != exit_condition)
Any loop as for, while, or even goto can do this job. If you put a condition instead of "true" in the loop, You can reduce code and doesn't need to use the "break" statement.