22

I'd like something like this:

each[i_, {1,2,3},
  Print[i]
]

Or, more generally, to destructure arbitrary stuff in the list you're looping over, like:

each[{i_, j_}, {{1,10}, {2,20}, {3,30}},
  Print[i*j]
]

Usually you want to use Map or other purely functional constructs and eschew a non-functional programming style where you use side effects. But here's an example where I think a for-each construct is supremely useful:

Say I have a list of options (rules) that pair symbols with expressions, like

attrVals = {a -> 7, b -> 8, c -> 9}

Now I want to make a hash table where I do the obvious mapping of those symbols to those numbers. I don't think there's a cleaner way to do that than

each[a_ -> v_, attrVals, h[a] = v]

Additional test cases

In this example, we transform a list of variables:

a = 1;
b = 2;
c = 3;
each[i_, {a,b,c}, i = f[i]]

After the above, {a,b,c} should evaluate to {f[1],f[2],f[3]}. Note that that means the second argument to each should be held unevaluated if it's a list.

If the unevaluated form is not a list, it should evaluate the second argument. For example:

each[i_, Rest[{a,b,c}], Print[i]]

That should print the values of b and c.

Addendum: To do for-each properly, it should support Break[] and Continue[]. I'm not sure how to implement that. Perhaps it will need to somehow be implemented in terms of For, While, or Do since those are the only loop constructs that support Break[] and Continue[].

And another problem with the answers so far: they eat Return[]s. That is, if you are using a ForEach loop in a function and want to return from the function from within the loop, you can't. Issuing Return inside the ForEach loop seems to work like Continue[]. This just (wait for it) threw me for a loop.

Jack Bashford
  • 43,180
  • 11
  • 50
  • 79
dreeves
  • 26,430
  • 45
  • 154
  • 229

7 Answers7

11

I'm years late to the party here, and this is perhaps more an answer to the "meta-question", but something many people initially have a hard time with when programming in Mathematica (or other functional languages) is approaching a problem from a functional rather than structural viewpoint. The Mathematica language has structural constructs, but it's functional at its core.

Consider your first example:

ForEach[i_, {1,2,3},
  Print[i]
]

As several people pointed out, this can be expressed functionally as Scan[Print, {1,2,3}] or Print /@ {1,2,3} (although you should favor Scan over Map when possible, as previously explained, but that can be annoying at times since there is no infix operator for Scan).

In Mathematica, there's usually a dozen ways to do everything, which is sometimes beautiful and sometimes frustrating. With that in mind, consider your second example:

ForEach[{i_, j_}, {{1,10}, {2,20}, {3,30}},
  Print[i*j]
]

... which is more interesting from a functional point of view.

One possible functional solution is to instead use list replacement, e.g.:

In[1]:= {{1,10},{2,20},{3,30}}/.{i_,j_}:>i*j
Out[1]= {10,40,90}

...but if the list was very large, this would be unnecessarily slow since we are doing so-called "pattern matching" (e.g., looking for instances of {a, b} in the list and assigning them to i and j) unnecessarily.

Given a large array of 100,000 pairs, array = RandomInteger[{1, 100}, {10^6, 2}], we can look at some timings:

Rule-replacement is pretty quick:

In[3]:= First[Timing[array /. {i_, j_} :> i*j;]]
Out[3]= 1.13844

... but we can do a little better if we take advantage of the expression structure where each pair is really List[i,j] and apply Times as the head of each pair, turning each {i,j} into Times[i,j]:

In[4]:= (* f@@@list is the infix operator form of Apply[f, list, 1] *)
    First[Timing[Times @@@ array;]]
Out[4]= 0.861267

As used in the implementation of ForEach[...] above, Cases is decidedly suboptimal:

In[5]:= First[Timing[Cases[array, {i_, j_} :> i*j];]]
Out[5]= 2.40212

... since Cases does more work than just the rule replacement, having to build an output of matching elements one-by-one. It turns out we can do a lot better by decomposing the problem differently, and take advantage of the fact that Times is Listable, and supports vectorized operation.

The Listable attribute means that a function f will automatically thread over any list arguments:

In[16]:= SetAttributes[f,Listable]
In[17]:= f[{1,2,3},{4,5,6}]
Out[17]= {f[1,4],f[2,5],f[3,6]}

So, since Times is Listable, if we instead had the pairs of numbers as two separate arrays:

In[6]:= a1 = RandomInteger[{1, 100}, 10^6];
        a2 = RandomInteger[{1, 100}, 10^6];

In[7]:= First[Timing[a1*a2;]]
Out[7]= 0.012661

Wow, quite a bit faster! Even if the input wasn't provided as two separate arrays (or you have more than two elements in each pair,) we can still do something optimal:

In[8]:= First[Timing[Times@@Transpose[array];]]
Out[8]= 0.020391

The moral of this epic is not that ForEach isn't a valuable construct in general, or even in Mathematica, but that you can often obtain the same results more efficiently and more elegantly when you work in a functional mindset, rather than a structural one.

dreeves
  • 26,430
  • 45
  • 154
  • 229
Michael Pilat
  • 6,480
  • 27
  • 30
  • 1
    Beautifully said. Thanks for this contribution. An example of where I find ForEach useful is creating a hash table. Say I have a list of data structures; then I could walk through them with, say, `ForEach[{i_, {j_, k_}}, listofstuff, hash[f[i,k] = g[j,k]]`. – dreeves Mar 07 '10 at 00:41
9

Newer versions of Mathematica (6.0+) have generalized versions of Do[] and Table[] that do almost precisely what you want, by taking an alternate form of iterator argument. For instance,

Do[
  Print[i],
  {i, {1, 2, 3}}]

is exactly like your

ForEach[i_, {1, 2, 3,},
  Print[i]]

Alterntatively, if you really like the specific ForEach syntax, you can make a HoldAll function that implements it, like so:

Attributes[ForEach] = {HoldAll};

ForEach[var_Symbol, list_, expr_] :=
  ReleaseHold[
    Hold[
      Scan[
        Block[{var = #},
         expr] &,
      list]]];

ForEach[vars : {__Symbol}, list_, expr_] :=
  ReleaseHold[
    Hold[
      Scan[
        Block[vars,
          vars = #;
          expr] &,
      list]]];

This uses symbols as variable names, not patterns, but that's how the various built-in control structures like Do[] and For[] work.

HoldAll[] functions allow you to put together a pretty wide variety of custom control structures. ReleaseHold[Hold[...]] is usually the easiest way to assemble a bunch of Mathematica code to be evaluated later, and Block[{x = #}, ...]& allows variables in your expression body to be bound to whatever values you want.

In response to dreeves' question below, you can modify this approach to allow for more arbitrary destructuring using the DownValues of a unique symbol.

ForEach[patt_, list_, expr_] := 
  ReleaseHold[Hold[
     Module[{f}, 
       f[patt] := expr; 
       Scan[f, list]]]]

At this point, though, I think you may be better off building something on top of Cases.

ForEach[patt_, list_, expr_] :=
  With[{bound = list},
    ReleaseHold[Hold[
       Cases[bound,
         patt :> expr]; 
       Null]]]

I like making Null explicit when I'm suppressing the return value of a function. EDIT: I fixed the bug pointed out be dreeves below; I always like using With to interpolate evaluated expressions into Hold* forms.

Pillsy
  • 9,781
  • 1
  • 43
  • 70
  • I believe this doesn't do the destructuring though. Ie, you can't iterate over data structures where you give the pieces of the data structures their own names, like in my example of iterating over {i,j} pairs. I find that super useful. – dreeves Aug 11 '09 at 20:47
  • Is there any advantage to your Hold/ReleaseHold method vs my version that uses an Evaluate? I guess either way you have to set the HoldAll attribute? This is basically Mathematica's version of Lisp macros. It would be nice to have a more general/canonical way to do it. Maybe that's what you've given? – dreeves Aug 11 '09 at 20:58
  • The second rule does the destructuring, because Set[] destructures automatically: {i, j} = {3, 4} will set i to 3 and j to 4. I usually prefer the Hold/ReleaseHold approach whenever possible, especially if I can directly substitute things in as function parameters, because it spares me from having to think about what's held and what isn't. And yes, this is basically a somewhat clunky way of doing Lisp macros. – Pillsy Aug 12 '09 at 00:28
  • Thanks, this is an edifying discussion! I believe my version is still more general in that it can destructure arbitrary nested structures. Can your version be patched to do that? – dreeves Aug 26 '09 at 05:41
  • Yes, I've edited my answer to allow arbitrary, pattern-based destructuring. The more I think about it, though, the more I think Cases is pretty much exactly what you want. – Pillsy Aug 26 '09 at 13:02
  • Wow, I just did a speed test and your version with Hold and Cases seems to be over 4 times faster than my version with Scan/Replace/Evaluate. Thanks so much! I'm making yours the accepted answer now. – dreeves Mar 03 '10 at 18:55
  • Oops, I found a bug in your Hold/Cases version. If I have a pre-defined list I want to iterate over then, due to the Holds, it doesn't work. Eg, `nums = {1,23}; ForEach[i_, nums, Print[i]];` I guess you just need to do `Evaluate[list]` like I did in my version. Want to update your answer? – dreeves Mar 05 '10 at 18:52
  • @Pillsy: I may have jumped to a wrong conclusion. I'm having trouble reproducing the supposed bug. Do you know of a case that fails without the With block you just added? – dreeves Mar 05 '10 at 20:43
  • @Pillsy: I now believe the supposed bug to have been a false alarm. I had changed the LHS of the ForEach definition to say `ForEach[pat_, lst_List, bod_]` which you can't do for a HoldAll function. Eg, `SetAttributes[f, HoldAll]; f[x_List]:=0; abc={1,2,3}; f[abc]` doesn't evaluate. – dreeves Mar 05 '10 at 21:01
8

The built-in Scan basically does this, though it's uglier:

    Scan[Print[#]&, {1,2,3}]

It's especially ugly when you want to destructure the elements:

    Scan[Print[#[[1]] * #[[2]]]&, {{1,10}, {2,20}, {3,30}}]

The following function avoids the ugliness by converting pattern to body for each element of list.

SetAttributes[ForEach, HoldAll];
ForEach[pat_, lst_, bod_] :=  Scan[Replace[#, pat:>bod]&, Evaluate@lst]

which can be used as in the example in the question.

PS: The accepted answer induced me to switch to this, which is what I've been using ever since and it seems to work great (except for the caveat I appended to the question):

SetAttributes[ForEach, HoldAll];             (* ForEach[pattern, list, body]   *)
ForEach[pat_, lst_, bod_] := ReleaseHold[    (*  converts pattern to body for  *)
  Hold[Cases[Evaluate@lst, pat:>bod];]];     (*   each element of list.        *)
dreeves
  • 26,430
  • 45
  • 154
  • 229
  • 1
    Daniel, you don't need Evaluate here, as well as ReleaseHold/Hold pair. Sorry for being repetitive, but it looks like you are not a big fan of extra typing. Evaluate is not needed since Cases does not hold arguments, and HoldAll for ForEach only guarantees holding args inside ForEach. Cases initiates a new evaluation cycle, on which that HoldAll has no effect. ReleaseHold@Hold[] is meanigless in general (as I noted elsewhere), and here particularly, since SetDelayed does not evaluate its r.h.s. In fact, you may want the opposite: wrap your lst in Unevaluated - makes more sense to me – Leonid Shifrin Jan 14 '11 at 00:01
  • @Leonid: This sounds right, though I feel like there was some reason -- a subtle bug perhaps -- that I ended up adding the extra Evaluate and Hold and whatnot. Want to add your simplified version as an answer and I'll test it and mark it accepted if I can convince myself it's better? Thanks again for helping with this! – dreeves Jan 14 '11 at 00:08
  • 1
    Sure, why not. I am much less concerned with earning a credit for it, than with ensuring that misconceptions don't propagate, so I'd just be happy if (once you are convinced this is correct) you could make this more visible for others visiting this page, by whatever means, not necessarily by checkmarking it. Let me just admit that I was using ReleaseHold[Hold[]] and Evaluate in a similar fashion for quite a while, until I understood Mathematica evaluation much better and realized that they are unnecessary (at least for this sort of use cases). – Leonid Shifrin Jan 14 '11 at 00:33
3

The built-in Map function does exactly what you want. It can be used in long form:

Map[Print, {1,2,3}]

or short-hand

Print /@ {1,2,3}

In your second case, you'd use "Print[Times@@#]&/@{{1,10}, {2,20}, {3,30}}"

I'd recommend reading the Mathematica help on Map, MapThread, Apply, and Function. They can take bit of getting used to, but once you are, you'll never want to go back!

kchoose2
  • 796
  • 1
  • 6
  • 13
  • 1
    Thanks! Map is indeed almost always what you want for such things. Scan is actually identical to Map except that it's used strictly for the side effects -- it doesn't return the list. – dreeves Nov 23 '08 at 06:14
2

Here is a slight improvement based on the last answer of dreeves that allows to specify the pattern without Blank (making the syntax similar to other functions like Table or Do) and that uses the level argument of Cases

SetAttributes[ForEach,HoldAll];
ForEach[patt_/; FreeQ[patt, Pattern],list_,expr_,level_:1] :=
   Module[{pattWithBlanks,pattern},
      pattWithBlanks = patt/.(x_Symbol/;!MemberQ[{"System`"},Context[x]] :> pattern[x,Blank[]]);
      pattWithBlanks = pattWithBlanks/.pattern->Pattern;

      Cases[Unevaluated@list, pattWithBlanks :> expr, {level}];
      Null
   ];

Tests:

ForEach[{i, j}, {{1, 10}, {2, 20}, {3, 30}}, Print[i*j]]
ForEach[i, {{1, 10}, {2, 20}, {3, 30}}, Print[i], 2]
faysou
  • 1,142
  • 11
  • 25
1

Mathematica have map functions, so lets say you have a function Functaking one argument. Then just write

Func /@ list

Print /@ {1, 2, 3, 4, 5}

The return value is a list of the function applied to each element in the in-list.

PrimeQ /@ {10, 2, 123, 555}

will return {False,True,False,False}

Per Alexandersson
  • 2,443
  • 1
  • 22
  • 28
  • Thanks, yes, Map (`/@`) is normally what you want. See my comment to the answer posted Nov 23 though. – dreeves Aug 26 '09 at 05:25
1

Thanks to Pillsy and Leonid Shifrin, here's what I'm now using:

SetAttributes[each, HoldAll];               (* each[pattern, list, body]      *)
each[pat_, lst_List, bod_] :=               (*  converts pattern to body for  *)
  (Cases[Unevaluated@lst, pat:>bod]; Null); (*   each element of list.        *)
each[p_, l_, b_] := (Cases[l, p:>b]; Null); (* (Break/Continue not supported) *)
Community
  • 1
  • 1
dreeves
  • 26,430
  • 45
  • 154
  • 229
  • Daniel, since your each is HoldAll, using patterns like lst_List will let you down if the list is stored in a variable or is a result of evaluation of some other function, for example: a = {b,c,d}; {b,c,d} = {1,2,3};each[x_,a,x=1]. In fact, in this case your first def will not match and then second def will let you down. The reason why pattern-matching interferes with Hold-attributes, I discussed here: http://www.mathprogramming-intro.org/book/node408.html. I'd just stick with SetAttributes[each, HoldAll]; each[p_, l_, b_] := (Cases[Unevaluated[l], p :> b];); , and no Null at the end – Leonid Shifrin Jan 17 '11 at 13:47
  • @Leonid: My thinking was that I would just not expect the items in the list to remain unevaluated unless I passed in an explicit list. In your example, I'm not being let down, as you say, because by passing in "a" I'm really passing {1,2,3} so it makes sense that it fails to set those elements. – dreeves Jan 17 '11 at 15:16
  • You are right. I was too quick to comment this time, and did not quite get the semantics of your function. Also in my version, it would not work as desired, since it is very hard to "partially evaluate", so that a evaluates into {b,c,d} but they don't evaluate further, and even that would be ambiguous. I can remove my comment. What is the best thing to do with such wrong comments on SO - keep them or remove them? – Leonid Shifrin Jan 17 '11 at 15:39
  • @Leonid: Phew! Thanks! As for comments, if it were in the question or answer I would definitely say delete. But comments I'm not sure. I should add some more exposition to the answer to head off such confusion in the first place. (Or feel free to do so if you have enough magic points to edit other people's answers!) – dreeves Jan 17 '11 at 16:40