Mathematica Programming » Code Structure
Scoping Revisited
With, Module, and Block
We finally have enough knowledge to understand the differences between the scoping constructs. We'll start by writing the same chunk of code with each:
Module[{a=35},
a^2
]
1225
With[{a=35},
a^2
]
1225
Block[{a=35},
a^2
]
1225
Everything is normal as of now. So let's try introducing some held expressions:
Module[{a=35},
a^2//Hold
]
Hold[a$24242]
With[{a=35},
a^2//Hold
]
Hold[352]
Block[{a=35},
a^2//Hold
]
Hold[a2]
This alone should help in figuring out what is going on:
Modulecreates a new symbol with$ModuleNumberappendedWithinserts the given values into the expression
But what is Block doing? Nothing seems changed.
Let's look at a different example, just considering Module and Block as there is little more to discuss with With , although it should be noted that With is the cleanest and most useful of all these constructs.
Let's try define a global function:
scopeProbe[x_]:=x*b;
First let's use it inside Module
Module[{b=35},
scopeProbe[10]
]
10 b
This behavior makes perfect sense. The b defined in Module is not the global b so this is what we'd predict.
Block[{b=35},
scopeProbe[10]
]
350
This here is the critical difference between Module and Block . Block reassigns the values of its symbols temporarily, while Module makes new symbols with temporary values (they have been given the attribute Temporary ).
This makes Block incredibly useful, but also potentially dangerous. Names used as variables to Block should always be made unique enough such that it's unlikely they'll have been used in a function that will be called by the Block .
An example of how we can use this is in memoization for a recursive function:
recursiveFunction[arg_]:=Block[{`recursiveFunction`memoPad=<||>},
recursiveStep[arg]
];
recursiveStep[arg_]:=
If[!KeyMemberQ[`recursiveFunction`memoPad,arg],
`recursiveFunction`memoPad[arg]=If[Length@arg==0,
Pause[.5];
Total@ToCharacterCode@ToString@arg,
recursiveStep/@(List@@arg)//Total
],
`recursiveFunction`memoPad[arg]
];
And to see that this is doing what we expect
recursiveFunction[a[a,a,a,a,a]]//AbsoluteTiming
{0.500925`,485}
While without memoization this should take about 2.5 seconds:
recursiveNoMemo[arg_]:=If[Length@arg==0,
Pause[.5];
Total@ToCharacterCode@ToString@arg,
recursiveNoMemo/@(List@@arg)//Total
]
recursiveNoMemo[a[a,a,a,a,a]]//AbsoluteTiming
{2.506773`,485}
And just to check that we the Block wrapper does something:
recursiveStep@a[a,a,a,a,a]
If[!KeyMemberQ[Global`recursiveFunction`memoPad,a[a,a,a,a,a]],Global`recursiveFunction`memoPad[a[a,a,a,a,a]]=If[Length[a[a,a,a,a,a]]==0,Pause[0.5`];Total[ToCharacterCode[ToString[a[a,a,a,a,a]]]],Total[recursiveStep/@List@@a[a,a,a,a,a]]],Global`recursiveFunction`memoPad[a[a,a,a,a,a]]]