9

For reusable code it's good practice to use Module[] to localize iteration variables used by Table[], as explained by this post.

Is it possible to use some Hold / ReleaseHold gymnastics to write a function ModuleTable[] which behaves like this?

ModuleTable[f[i], {i, 1, 10}]

gets automatically expanded into

Module[{i}, Table[f[i], {i, 1, 10}]]
Jagra
  • 14,343
  • 1
  • 39
  • 81
felix
  • 549
  • 2
  • 5
  • 3
    In the simplest case of a single iteration variable, what you ask for is as simple as SetAttributes[ModuleTable, HoldAll]; ModuleTable[code_, iter : {var_, __}] := Module[{var}, Table[code, iter]]. Generalizing to several iteration variables is not hard either. – Leonid Shifrin Apr 21 '21 at 21:54
  • Thanks for the answer. I figured out how to handle any number of iterations. SetAttributes[ModuleTable, HoldAll]; ModuleTable[code_, iters : ({_, __} ..)] := Module[Evaluate[{iters}[[All, 1]]], Table[code, iters]]; – felix Apr 22 '21 at 10:16
  • 2
    @felix not so simple though, try i = 5; ModuleTable[i, {i, 10}] – Kuba Apr 22 '21 at 10:34
  • @Kuba You're right. Do you have a quick solution? It's easy to deal with a fixed number of 2 iterations, 3 iterations etc., but I haven't figured out how to do it for an arbitrary number of iterations. – felix Apr 22 '21 at 10:46

1 Answers1

10

Here is one way to do this for arbitrary number of iterators:

ClearAll[ModuleTable]
SetAttributes[ModuleTable, HoldAll];
ModuleTable[code_, iters__List] :=
  Replace[
    DeleteCases[Hold[iters][[All, 1]], Except[_Symbol]], 
    Hold[vars__] :> Module[{vars}, Table[code, iters]]
  ]

where DeleteCases was necessary since iterators may not include a symbol. The main device to control code evaluation, used here, is called injector pattern on this site (you can search for this term to see other examples of its use).

For example:

ClearAll[a, b]
a = 1;
b = 2;
ModuleTable[a + b, {a, 1, 3}, {2}, {b, 2, 5}]

(* {{{3, 4, 5, 6}, {3, 4, 5, 6}}, {{4, 5, 6, 7}, {4, 5, 6, 7}}, {{5, 6, 7, 8}, {5, 6, 7, 8}}} *)

Leonid Shifrin
  • 114,335
  • 15
  • 329
  • 420