5
$\begingroup$

How could I Break from only the innermost loop in a

Do[stuff,{i1,i1min,i1max},{i2,i2min,i2max},...]

?

I get the desired behaviour if I use

Do[Do[...Do[stuff,{i1,i1min,i1max}],...]

but this hard to utilize because the number of loops in my program is not fixed, and therefore I normally create a Table the iterator specs with a function myiterators[n] where n is the number of loops, and use

Do[stuff,Evaluate[Sequence@@myiterators[n]]]

If stuff contains a Break here, it breaks all n loops... Instead of only generating the iterators, I could of course generate the full Do[Do[... structure using Fold or similar, but this does not seem like the best way.

EDIT AFTER LEONID'S ANSWER:

In my case, the function myiterators contains a call to a compiled function to evaluate the upper limits of the iterators. This precludes the use of Evaluate[Sequence@@...] since Evaluate forces the evaluation of the compiled function before the iterators aquire values in mulitDo. My solution was to use

ClearAll[multiDo2];
SyntaxInformation[
   multiDo2] = {"LocalVariables" -> {"Table", {2, Infinity}}};
SetAttributes[multiDo2, HoldAll];
multiDo2[code_, {rest___, iter_}] := 
  multiDo2[Do[code, iter], {rest}];
multiDo2[code_, {}] := code;

and call it with

multiDo2[stuff,myiterators[n]]

Works like a charm!

$\endgroup$
  • 2
    $\begingroup$ Use Catch & Throw instead of Break. $\endgroup$ – Chris Degnen May 14 '15 at 16:01
  • $\begingroup$ @ChrisDegnen, how? Catch[Do[If[j > 7, Throw[Null]]; Print[{i, j}], {i, 9}, {j, i + 1, 10}]] never reaches i>1... $\endgroup$ – Marius Ladegård Meyer May 14 '15 at 16:04
  • $\begingroup$ See Catch - Generalisations & Extensions for use of tags. $\endgroup$ – Chris Degnen May 14 '15 at 16:07
10
$\begingroup$

I think this is a good case for a simple code generation. Here is one way to generate multiple Do in a macro-like fashion:

ClearAll[multiDo];
SyntaxInformation[multiDo] = {"LocalVariables" -> {"Table", {2, Infinity}}};
SetAttributes[multiDo, HoldAll];
multiDo[code_,  rest___, iter_] := multiDo[Do[code, iter], rest];
multiDo[code_] := code;

Basically, multiDo is a new language construct. It constructs the nested Do code at run-time, without evaluating the body, and then evaluates the code, after it has been fully constructed. Notice that (as MichaelE2 pointed out in comments), the outer-most iterator in the multiple-iterator syntax becomes the inner-most one in the generated code.

Here is an example:

sum = 0;
multiDo[If[i > j, Break[], sum += i], {j, 10}, {i, 10}];
sum

(* 220 *)

In this case, similar result can be accomplished by

sum = 0;
Do[sum += i, {i, 10}, {j, i, 10}]
sum

(* 220 *)

but in general, one can't easily reduce the multiDo to a simple Do with multiple iterators.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Doesn't the normal order of iteration have the last iterator be the inside iterator? E.g. multiDo[code_, rest___, iter_] := (* same *)? $\endgroup$ – Michael E2 May 14 '15 at 16:27
  • $\begingroup$ @MichaelE2 You are right, nicely spotted! Will edit. $\endgroup$ – Leonid Shifrin May 14 '15 at 16:30
  • 2
    $\begingroup$ @LeonidShifrin, thanks for teaching me this way of doing it. I discovered that Do[Catch[stuff;Throw[something]],i1spec,i2spec,...] also seems to work. Do you know why? I don't understand how Catch somehow is outside only the innermost loop. $\endgroup$ – Marius Ladegård Meyer May 14 '15 at 16:40
  • $\begingroup$ @MariusLadegårdMeyer Indeed, it works, although it's not obvious at all that it should. I can well imagine an implementation of Do[body, iter1, ..., itern], which would satisfy all the Do specs but where this won't work. So, unless I am missing something, this means relying on hidden deatails or an implementation (which one also often does, but which is generally considered not to be a great practice). $\endgroup$ – Leonid Shifrin May 14 '15 at 17:20
  • 1
    $\begingroup$ Another advantage of the method I proposed is that it won't be too hard to get it into Compile, if needed. $\endgroup$ – Leonid Shifrin May 14 '15 at 17:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.