How to break several nested 'for' loop in one 'if'

Hi community,
Available manual and posts gave me some hints of break for ‘for’ loop. However, intending to break 2nd and 3rd ‘for’ loops and return to the 1st ‘for’ loop, two breaks don’t work here.

s0=0.05; N=1e6;
violation=Int64;
x1 #allele frequency array

for a in 1:rows #except wild type
#two breaks intend to return to 'for a in …'

for b in 1:col#column
if x1[a,b] > 1/(2Ns0)
for c in b+1:col
if x1[a,c]==0 #go extinction
push!(violation,a)
break #for c in …
break #for b in …
end
end
end
end
end
print(violation)

Thanks,
Amend

You would need to do some other way than break. Possibilities are @goto and just setting some variable and then look at that variable in the outer loop and break there.

3 Likes

A reply along the same line was recently given here.

Related reading: https://github.com/JuliaLang/julia/issues/5334

2 Likes

You can write it as a function and return.

5 Likes

I see! This works.

(function()
for i = 1:100
for j = 1:100
return
end
end
end)()

Codes as above is from the link https://github.com/JuliaLang/julia/issues/5334 suggested by @GunnarFarneback. Thank you~

1 Like

Thank you. This works.

for i=1:3
for j=1:3
for k=1:3
@info “indices” (i, j, k)
if k == 2
@goto end_j
end
end
end
@label end_j
end

Codes quoted from “@c42f” on https://github.com/JuliaLang/julia/issues/5334#issuecomment-39616744

I decided to turn my gist from https://github.com/JuliaLang/julia/issues/5334#issuecomment-206508859 into a package. If you do

using Pkg; Pkg.add("https://github.com/GunnarFarneback/Multibreak.jl")

you should be able to break twice as you wanted with help from the @multibreak macro. See the tutorial for documentation.

4 Likes

This is exactly what I am looking for. Thanks a lot!

using Pkg; Pkg.add(“https://github.com/GunnarFarneback/Multibreak.jl”)
using Multibreak
out =
@multibreak for i = 1:5
for j = 1:3
if i == 1 && j == 1
continue
elseif i == 2 && j == 2
break
elseif i == 3 && j == 2
break; continue
elseif i == 4 && j == 2
break; break
end
push!(out, (i, j))
end
push!(out, i)
end

@test out == [(1, 2), (1, 3), 1,
              (2, 1), 2,
              (3, 1),
              (4, 1)]

end

I wonder if it’s worthwhile to make a macro to break to specific label?

@mark a for i in I
    for j in J
        ...
        @break a
    end
end

2 Likes

It would be great to have something like common-lisp:return-from, ie a named block that would return that value when return-from is invoked inside.

I am not sure if it can be done with a macro in a compiler-friendly manner though.

2 Likes

I bet we can have this:(EDIT: with only macro)

a = 1
b = 2
@mark label=l outer_vars=[a] for i in I
     a = 11
     b = 22
     @break l
end
# a = 11
# b = 2

We just cannot share scopes automatically.

Just for my interest to ask, what’s the difference between ‘@multibreak begin end’ (I thought it functions by introducing a macro?) and a made macro ‘@mark @break’ as you suggested?

Is the bad reputation of ‘@goto @label’ the reason of a special call ‘@break’?

This is now a registered package, so no need to add it by URL any longer.

In my opinion the main difference is that @multibreak doesn’t require coming up with any additional names for loops or labels. Having names may make things clearer if the nesting is very deep or complicated but let’s not fool ourselves by the generality of the construction. I expect that 90% of the use cases for @multibreak is break; break, 9% break; continue and at most a small fraction goes any deeper. In the rare cases where the loops are so convoluted that @multibreak control flow would be hard to follow I would just use the existing @goto and @label macros.

2 Likes

Had same problem as OP to exit from two nested loops and easiest way seems to be to use the @goto and @label macros as suggested above by @kristoffer.carlsson.

Could not find much information on the Julia manual about this and would like to ask if this is a common (/recommended) practice in Julia language?

Thank you.

It is a common and recommended practice in Fortran77 and older :slight_smile: . Since then the recommended is to avoid having to to that in some whay that the code keeps modular. (of course, that is only important when anyone will ever have to read and follow your code…)

(jokes apart, there are many cases where it is so much more natural to do that, that, you can see, workarounds still abound).

1 Like

I use the Multibreak package every time I need to break out of two loops, but I may be biased.

I suspect using a helper variable for the control flow is the most common solution, unless it’s easy to place the loops in a subfunction you can return from or you can loop over multiple variables in a single for loop (in which case a single break suffices). I rarely see the @goto and @label macros in the wild and when I’ve seen them it has been for more complex control flow than just breaking out of two loops.

3 Likes

@GunnarFarneback, thank you for your valuable answer.

You made me rethink the program and improve it.
Reorganizing the code and using a while inner loop and a helper variable instead of the for loop, was enough to get rid of the @goto macro.
There should be a good reason why this macro does not appear in Julia’s manual Control Flow section.

Regarding your nice Multibreak.jl package, the tutorial documentation scared the hell out me:

A word of warning. The @multibreak macro also transforms single break and continue to @goto and @label. A side effect of the implementation is that dead code can come alive.

(It sounds like the return of the living dead! :ghost:)

Thanks again :slightly_smiling_face:

1 Like

There is some truth in my joke above. The criticisms certainly may not apply to a very specific and local use of labels and go-tos, but in general it is a code layout that is better avoided, because it makes sections of the code hard to understand and reuse.

Goto - Wikipedia(goto%2C%20GOTO%2C%20GO,function%20call%20normally%20returns%20control.

1 Like