4

Let's say I have I several nested for loops in Ceylon. How do I break out of all the loops:

variable Integer? something = null;
for (i in 0:3) {
  for (j in 0:3) {
    for (k in 0:3) {
      something = someFunction(i,j,k);
      if (something exists) {
        // break out of everything, we found it
      }
    }
  }
}
drhagen
  • 8,331
  • 8
  • 53
  • 82

3 Answers3

6

One way to do it is to wrap the whole thing in a closure and then call it using return when you want to break out of everything:

Integer? doLoops() {
  for (i in 0:3) {
    for (j in 0:3) {
      for (k in 0:3) {
        Integer? something = someFunction(i,j,k);
        if (something exists) {
          return something;
        }
      }
    }
  }
  return null;
}
Integer? something = doLoops();

Because Ceylon supports closures, you can also read and write other values outside the function in the scope where doLoops is defined.

drhagen
  • 8,331
  • 8
  • 53
  • 82
5

You can use comprehension and stream processing syntax instead:

Iterable<Integer?> stream = {
    for (i in 0:3) for (j in 0:3) for (k in 0:3) someFunction(i,j,k)
};

Integer? = stream.coalesced.first;

This will work as expected because { for ... } comprehension creates an Iterable that is lazily evaluated.

The coalesced attribute of the iterable return another lazy iterable that skips all null values in the original.

Then just ask for the first value of the remaining iterable and there's your answer.

Roland Tepp
  • 8,301
  • 11
  • 55
  • 73
3

Another way to do it is to use else blocks on the for loops:

shared void run() {
    variable Integer? x = null;
    for (i in 0:3) {
        for (j in 0:3) {
            for (k in 0:3) {
                value n = i*j*k;
                if (n > 18) {
                    x = n;
                    break;
                } else {
                    continue;
                }
            } else {
                continue;
            }
            break;
        } else {
            continue;
        }
        break;
    } else {
        x = null;
    }
    print(x);
}

In general, place

else {
    continue;
}
break;

after every for’s closing brace.

(Note: Ideally, the variable being assigned – x in my example, something in yours – wouldn’t need to be variable, since it’s assigned exactly once. However, the typechecker currently can’t prove that.)

How does it work? The else after a for loop is executed iff that loop ran to completion without any break. In that case, we want to continue the outer loop as well; otherwise – that is, if we broke from the inner loop – we want to break from the outer one as well.

This could be written more succinctly with the following syntax proposed in ceylon/ceylon#3223:

for (a in b) {
    for (x in y) {
        if (something) {
            break;
        }
    } then { // opposite of else: runs iff there was a break in the inner loop
        break;
    }
}

I wouldn’t recommend this solution, for three reasons:

  • It’s even less readable than your closure solution. The for {} else {} feature is fairly obscure, and the meaning isn’t obvious at all if you’re not familiar with it, making the code incomprehensible to the casual reader.
  • It bloats the code a lot in every surrounding loop (not just once), also making it unreadable.
  • If you want to have code in the outer loops after the inner loops, you’ll have to duplicate it: the else block and the code after it both need a copy.
Lucas Werkmeister
  • 2,584
  • 1
  • 17
  • 31