2
$\begingroup$

In the documentation for the Do[...] function, a loop of nesting up to any number of layers is supported. But to apply their function, one needs to know ahead of time how many layers of loops they're using. Is it possible to encode a nested loop where the number of layers is arbitrary?

For example, I want to do something like the following

Do[ (*tasks*),{v1, iters1}, {v2, iters2}] ...,, {vN, itersN} ]

where 'iters2' depends on $(v_1 \in \text{iters1})$, 'iters3' depends on both $(v_1 \in \text{iters1},v_2 \in \text{iters2})$, and so on up to 'itersN' that depends on all the $(v_1,\dots,v_{N-1})$. In particular, I want to do this where I don't know what $N$ is.

I can do this somewhat painstakingly by carefully keeping track of all the iterators and loop variables and carefully implementing counting. But it would be much nicer if this was possible natively. I tried using the apply '@@' function, but

Do@@{(*tasks*),{{v1,iters1},{v2,iters2},...,{vN,itersN}}}

doesn't work. E.g.

Do @@ {Print[i];, {i, 1, 5}}

simply prints the unassigned variable 'i'. Is there some short way to do what I'm asking?

Maybe an answer could illustrate this for all lists of $n$ ascending positive integers less than or equal to $2 n$, which is schematically

Do[Print[{i[1],...,i[N]}],{{v[1],1,2 n},{v[2],v[1]+1,2 n},...,{v[n],v[n-1]+1,2 n}}]

While I thought earlier that the listed comments/answers are sufficient, it turns out they're actually not useful for the case I'm interested in. Let's consider the following 'coloring' problem which is closer in flavor to what I want. (I changed the game slightly to be closer in flavor to the problem I'm having)

Color the integers $\{1,\dots,n\}$ as $\{c(1),...,c(n)\}$ which are one of four colors $c(i) \in \{\text{Red,Blue,Green,Black}\}$ with the following rules:

  • '1' and '2' can be colored anything you want
  • Given colorings on $(j-1,j-2)$, we are allowed to color '$j$' as any color that isn't $c(j-1)$ or $c(j-2)$. We can write this as $c(j) \in \ell(c(j-1)) \cap \ell(c(j-2))$, where $\ell(\text{color})=\{\text{Red,Blue,Green,Black}\} \backslash \{\text{color}\}$

Number the colors as $\text{Red}\to 1$,$\text{Blue}\to 2$,$\text{Green}\to 3$,$\text{Black}\to 4$. And call $v[j] \in \{1,2,3,4\}, j \in \{1,\dots,n\}$ a possible vector of colors if

MemberQ[Intersection[nextList[[v[j-1]]],nextList[[v[j-2]]]],v[j]]

where

nextList = {{2,3,4},{1,3,4},{1,2,4},{2,3,4}}

The problem with the listed answers is that there are errors when calling elements of a Table. However, following J.M.ennui's answer in the comments

nextList = {{2, 3, 4}, {1, 3, 4}, {1, 2, 4}, {1, 2, 3}};
n = 3;
Do[Table[v[i], {i, n}] // Print;, ##] & @@
 Table[{v[j], 
   If[j == 1 || j == 2, {1, 2, 3, 4}, 
    Intersection[Indexed[nextList, v[j - 1]],Indexed[nextList, v[j - 2]]]]}, {j, n}]

doesn't work (although the n=2 case works just fine). Nor does Bob Hanlon's answer work, or replacing each v[j] with Subscript[v,j]. But, the answers provided do work when I consider a different problem that doesn't involve the Intersection function. Is there a syntax that supports this more general question?

The errors that show up are:

Indexed::argr: Indexed called with 1 argument; 2 arguments are expected.

Do::iterb: Iterator {Subscript[v, 3],Indexed[{{2,3,4},{1,3,4},{1,2,4},{1,2,3}}]} does not have appropriate bounds.

$\endgroup$
6
  • 2
    $\begingroup$ I have done constructs like Do[(* stuff *), ##] & @@ Table[{K[j], 1, n}, {j, 5}] or Do[(* stuff *), ##] & @@ Prepend[Table[{K[j], 1, K[j + 1]}, {j, 4, 1, -1}], {K[5], 1, n}] a lot of times before. An acceptable alternative is to use e.g. IntegerPartitions[] or Tuples[] to generate the indices, and loop over those. $\endgroup$ Commented Jan 14, 2021 at 1:49
  • $\begingroup$ Thank you! This seems to be exactly what I'm looking for. $\endgroup$
    – Joe
    Commented Jan 14, 2021 at 1:59
  • $\begingroup$ Possible duplicates: (134), (51546) $\endgroup$
    – Michael E2
    Commented Jan 14, 2021 at 2:48
  • $\begingroup$ Actually, it seems that the answers provided aren't as general as I'd like and break down if the iterators have more complicated requirements. I'll edit the question in a few minutes accordingly with a more general example. $\endgroup$
    – Joe
    Commented Jan 14, 2021 at 2:52
  • 1
    $\begingroup$ Have you tried replacing nextList[[v[j - 1]]] with Indexed[nextList, v[j - 1]]? $\endgroup$ Commented Jan 14, 2021 at 3:49

2 Answers 2

2
$\begingroup$
Clear["Global`*"]

f[n_Integer?Positive] := Module[{v, iter},
  v[0] = 0;
  iter = Table[{v[k], v[k - 1] + 1, 2 n}, {k, 1, n}];
  Do[i++, Evaluate[Sequence @@ iter]]]

Table[i = 0; f[n]; i, {n, 5}]

(* {2, 6, 20, 70, 252} *)
$\endgroup$
2
$\begingroup$

I didn't read your entire question but I think this is what you want. I explained every step so it's also useful for other users.

n = 2;

(* generate list of variables v_1, v_2 etc *)
vlist = Table[Subscript[v, i], {i, 1, n}];

(* generate a list of iterators. This has the form { {v_1, { ... }}, {v_2, { ... } *)
(* you could replace 'iterators' with the code you want *)
iterlist = RandomInteger[10, {n, 3}];
iterators = Transpose@{vlist, iterlist};

(* The magic lies in Sequence@@ which I will explain shortly *)
iteratorsSeq = Sequence @@ iterators;
Do[Print[vlist], Evaluate[iteratorsSeq] ]

So how does Sequence@@ work? The operator @@ is short for Apply. Apply[f,expr] replaces the head of expr by f. Example: f@@{a,b,c} gives f[a,b,c]. In this case the old head was 'List' and the new head is 'f'. You can find the head of any expression by typing expr[[0]].

When the head of an expression is Sequence it is like it has no head at all. It distributes its contents into whatever expression it appears. Example:

f[a, Sequence@@{ b, c }, d] == f[a, Sequence[ b, c ], d] == f[a, b, c, d]

Bonus: you can also use iteratorsSeq in Table and Sum which I find very useful.

$\endgroup$
3
  • 1
    $\begingroup$ If you're fond of eliminating the middleman, Do[Print[vlist], ##] & @@ iterators works OK. $\endgroup$ Commented Jan 14, 2021 at 11:34
  • $\begingroup$ @J.M. That's a very nice solution. Wish I had known that earlier. $\endgroup$ Commented Jan 14, 2021 at 11:36
  • $\begingroup$ (reposted a comment from the question thread) It did indeed work for the problem I provided earlier. But, a slightly more complicated version doesn't work when I want v[j] to be in the intersection of multiple sets that depend on previous data (I updated the problem in the comments). Is there a way to handle this generalization? $\endgroup$
    – Joe
    Commented Jan 15, 2021 at 2:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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