Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

break more than 1 level of foreach nesting in tcl

Tags:

foreach

break

tcl

Is it possible to exit in 1 command, from two levels of nesting? That is, let us say I have this code:

foreach l { 1 2 3 4 } {
   foreach k { 3 4 5 6 } {
      if { $k > 4 } {
         break2
      } else {
         puts "$k $l"
   }
}

What that I would like to see output is:

1 3
1 4

The question is, how can one code the break2 (if possible)?.
I do not know of such "feature" in any language, other than wrapping this in a proc, and using return to stop the proc, which is more of a hack than proper language construct
Thanks.

like image 784
user1134991 Avatar asked Nov 03 '25 12:11

user1134991


2 Answers

It's not possible to do it directly; the break machinery doesn't have anything to trace up to anything other than the nearest looping context.

The easiest way of handling this is to use try in 8.6 and a custom exception code (i.e., any value from 5 upwards).

foreach a {b c} {
    puts "a=$a; to show that this is not stopping the outermost loop"
    try {
        foreach l { 1 2 3 4 } {
            foreach k { 3 4 5 6 } {
                if { $k > 4 } {
                    # Generating a custom exception code is a bit messy
                    return -level 0 -code 5
                }
                puts "$k $l"
            }
        }
    } on 5 {} {
        # Do nothing here; we've broken out
    }
}

Running that gives this output:

a=b; to show that this is not stopping the outermost loop
3 1
4 1
a=c; to show that this is not stopping the outermost loop
3 1
4 1

But it's pretty messy to do this; the best approach is typically to refactor your code so that you can just return ordinarily to end the loop. Using apply might make this easier:

foreach a {b c} {
    puts "a=$a; to show that this is not stopping the outermost loop"
    apply {{} {
        foreach l { 1 2 3 4 } {
            foreach k { 3 4 5 6 } {
                if { $k > 4 } {
                    return
                }
                puts "$k $l"
            }
        }
    }}
}

The downside of using apply is that it is a different variable context and has quite a bit more overhead (because of all the stack frame management). Still, the variable context thing can be worked around using upvar if you're careful.

like image 139
Donal Fellows Avatar answered Nov 06 '25 02:11

Donal Fellows


It's possible in Tcl ≥ 8.5:

foreach l { 1 2 3 4 } {
    foreach k { 3 4 5 6 } {
        if {$k > 4} {
            return -code break -level 2
        } else {
            puts "$k $l"
        }
    }
}

That return -code break -level 2 works like "make the enclosing command two levels up the stack return as if it has called break".

The return command manual page.

like image 20
kostix Avatar answered Nov 06 '25 02:11

kostix



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!