The main Mathematica scoping function to learn is Module. For occasional special purposes, use Block to temporarily store and then restore Global variables, and use With to fix local variables so they can't be accidentally changed inside of With.
Use any Doc Center page for a scratchpad (not Block), since all variable names and values are localized to that page and are automatically cleaned up when you leave the page.
Block does two things:
x=17;
Block[{x=4},x^2]
16
x
17
More typical uses of Block are to change the value of system environment variables such as decreasing $RecursionLimit or $IterationLimit to prevent infinite looping while you're debugging a function, or increasing them to solve a deeply nested or iterated calculation.
An interesting combinatorial series arises from the number of partitions of integer sets, the Bell number. Pemmaraju and Skiena compute it recursively. To let the function solve for indefinitely large sets, $RecursionLimit is temporarily set to Infinity within the Block and automatically restored to its default value after execution.
Clear[bellB,bellB1];
bellB@n_Integer:=bellB1@n;
bellB1@0=1;(*base case*)
bellB1@n_:=Block[
{$RecursionLimit=Infinity},
bellB1@n=Sum[Binomial[n-1,k]*bellB1@k,{k,0,n-1}] (*memo function*)
]
bellB/@Range@10
{1,2,5,15,52,203,877,4140,21147,115975}
$RecursionLimit
1024
x=25;
Here x isn't scoped and so isn't Blocked; Mathematica can't just assume you wanted it localized.
Block[{},x]
25
Here x is scoped but we didn't assign it a new value. So while its global value of 25 was stored in a temporary variable it was also used in Block.
Block[{x},x]
25
Now we assign a value to x in Block's scope, or in its body. Block works as intended.
Block[{x=5},x]
5
Block[{x},x=5]
5
And x's Global value was restored as Block closed itself out. All is good in the universe :-)
x
25
Block[{x=5},Print@x;x=50;x]
5
50
Module[{x=15},Print@x;x=100;x]
15
100
But With does not; we get an error message. When we try to Set x to 8, Mathematica sees "2 = 8":
With[{x=2},Print@x;x=8;x^2]
2
Set::setraw: Cannot assign to raw object 2. >>
Out[292]= 4
Block and Module immediately use scoped variable values, so here Mathematica sees "5^2 /. 5 -> 6", evaluates 5^2 as 25 and thus sees "25 /. 5 ->6"; there's no x to replace.
Block[{x=5},x^2/.x->6]
25
Module works by creating a special local variable name for each scoped variable. Here, within the Module, x lives on under the new name "x$485":
Module[{x},Print@x]
x$485
Use any Doc Center page for a scratchpad (not Block), since all variable names and values are localized to that page and are automatically cleaned up when you leave the page.
Block does two things:
- Temporarily stores a Global variable value and then restore it
- Uses a local value for the variable in the Block if you give it one
x=17;
Block[{x=4},x^2]
16
x
17
Step
|
Effect
|
x = 17;
|
Variable x is Set to 17 in the Global` Context
|
Block[{x=4},x^2]
|
A Block is entered using specifying a local variable with the same
name as the Global one (x = 4)
|
Within Block: x$23 = x
|
Local, temporary variable x$23 is Set to x’s global value of 17
|
Within Block: x = 4
|
Local, temporary variable x is Set to 4
|
Within Block: x^2
|
In Power[x, 2] x replaced by 4: Power[x, 2] /. x -> 4
|
Block returns 4^2 = 16
|
Locally with Block Power[x, 2] evaluates to 16, which is returned
|
Block Sets x = x$23 and exits
|
The Global value of x is restored
|
x
|
The Global values of x is called
|
17
|
The Global value of x is returned
|
Typical Uses of Block
Block is used to scope and protect the iterators in Table, Do, Sum, etc. If you write a function with an iterator you can use Block.More typical uses of Block are to change the value of system environment variables such as decreasing $RecursionLimit or $IterationLimit to prevent infinite looping while you're debugging a function, or increasing them to solve a deeply nested or iterated calculation.
An interesting combinatorial series arises from the number of partitions of integer sets, the Bell number. Pemmaraju and Skiena compute it recursively. To let the function solve for indefinitely large sets, $RecursionLimit is temporarily set to Infinity within the Block and automatically restored to its default value after execution.
Clear[bellB,bellB1];
bellB@n_Integer:=bellB1@n;
bellB1@0=1;(*base case*)
bellB1@n_:=Block[
{$RecursionLimit=Infinity},
bellB1@n=Sum[Binomial[n-1,k]*bellB1@k,{k,0,n-1}] (*memo function*)
]
bellB/@Range@10
{1,2,5,15,52,203,877,4140,21147,115975}
$RecursionLimit
1024
Why Block Might Not Seem to Work
Again, if you understand that Block will 1) temporarily store a Global variable value and then restore it, and 2) use a local value for the variable in the Block only if you give it one, you will understand the following examples. First, we Set x equal to 25 in Global`.x=25;
Here x isn't scoped and so isn't Blocked; Mathematica can't just assume you wanted it localized.
Block[{},x]
25
Here x is scoped but we didn't assign it a new value. So while its global value of 25 was stored in a temporary variable it was also used in Block.
Block[{x},x]
25
Now we assign a value to x in Block's scope, or in its body. Block works as intended.
Block[{x=5},x]
5
Block[{x},x=5]
5
And x's Global value was restored as Block closed itself out. All is good in the universe :-)
x
25
Block Compared with Module and With
Block and Module let us change scoped variable values in the body of their code.Block[{x=5},Print@x;x=50;x]
5
50
Module[{x=15},Print@x;x=100;x]
15
100
But With does not; we get an error message. When we try to Set x to 8, Mathematica sees "2 = 8":
With[{x=2},Print@x;x=8;x^2]
2
Set::setraw: Cannot assign to raw object 2. >>
Out[292]= 4
Block and Module immediately use scoped variable values, so here Mathematica sees "5^2 /. 5 -> 6", evaluates 5^2 as 25 and thus sees "25 /. 5 ->6"; there's no x to replace.
Block[{x=5},x^2/.x->6]
25
Module works by creating a special local variable name for each scoped variable. Here, within the Module, x lives on under the new name "x$485":
Module[{x},Print@x]
x$485
Your explanation was pleasantly clear. It clears up a lot. May I ask you for an explanation about this topic? I wanted to write a function that test if a function is even or odd. I end up writing a piece of code that seems to work but I don’t really know how. The 2nd line is the difficult one. I don’t quite get why have to be this way.
ReplyDeletepolyTest[exp_]:=
Module[{f},Activate@Inactive[SetDelayed][f[x_],exp];
Which[
f[x]===f[-x],Print["is even"],
-f[x]===f[-x],Print["is odd"],
True,Print["Nor even, nor odd"]
]]
Applying this function to an expression will look like:
polyTest[1/2 (E^x+E^-x)]
I previously wrote a more simple but (to me) clear code to do the same. But then I thought and if I write it as a function of Mathematica? What gave rise to this matter.
f[x_]:=1/2 (E^x-E^-x);
Which[
f[x]===f[-x],Print[f[x]," is Even"],
-f[x]===f[-x],Print[f[x]," is Odd"],
True,Print[f[x],"Nor even, nor odd"]
]
ClearAll[f]
Investigating I found that the 1st code could be called “nested scoping constructs”. My solution was an adaptation from various examples from mathematica.stackexchange.com, specifically at: https://mathematica.stackexchange.com/questions/180544/assign-a-function-within-a-function/
If you find some time for this, I will appreciate it, but even if not, this article is a thumb up.
Thank you.
Dear Fernando, I am not sure your solutions have to do with scoping. Scoping is automatic with SetDelayed and Pattern (e.g. f@x_:= x^2. For compound expressions it is good form to use Module or the more specialized Block or With.
DeleteI am not clear on why the Activate@Inactivate piece is required, but it does seem to be. Perhaps you can explain that. See some explorations below (you'll have to Evaluate the code).
Best,
Kris
Fernando - Even/Odd Function test
polyTest[exp_] := Module[{f}, Activate@Inactive[SetDelayed][f[x_], exp];
Which[f[x] === f[-x], Print["is even"], -f[x] === f[-x], Print["is odd"],
True, Print["Nor even, nor odd"]]]
Does it work without Activate@Inactivate?
polyTest2[exp_] := Module[{f}, SetDelayed[f[x_], exp];
Which[f[x] === f[-x], Print["is even"], -f[x] === f[-x], Print["is odd"],
True, Print["Nor even, nor odd"]]
]
A simple intuitive test
polyTest /@ {x^2, x^3}
Omitting Activate@Inactivate fails - why?
polyTest2 /@ {x^2, x^3}
Again, failure
polyTest2[1/2 (E^x + E^-x)]
Again, success
polyTest[1/2 (E^x - E^-x)]
polyTest2[(E^x + E^-x)] // Trace
The documentation doesn't explain how SameQ tests for equivalence other than syntactical equivalence. Reduce fails (see below).
-(E^x + E^-x) === (E^x + E^-x)
polyTest[(E^x + E^-x)] // Trace
(E^x + E^-x) == -(E^x + E^-x)
Reduce[(E^x + E^-x) == -(E^x + E^-x), x, Reals] // Trace
Reduce[(E^x - E^-x) == -(E^x + E^-x), x, Reals] // Trace
Plot[{E^x, E^-x}, {x, -2, 2}]
Plot[(E^x + E^-x), {x, -2, 2}, AxesOrigin -> {0, 0}]
Plot[(E^x - E^-x), {x, -2, 2}, AxesOrigin -> {0, 0}]
Plot[-(E^x + E^-x), {x, -2, 2}]
Your simpler function seems to work.
f2[x_] := 1/2 (E^x - E^-x);
Which[f2[x] === f2[-x], Print[f2[x], " is Even"], -f2[x] === f2[-x],
Print[f2[x], " is Odd"], True, Print[f2[x], "Nor even, nor odd"]]
f2[x_] := x^2; f3@x_ := x^3;
Which[f2[x] === f2[-x], Print[f2[x], " is Even"], -f2[x] === f2[-x],
Print[f2[x], " is Odd"], True, Print[f2[x], "Nor even, nor odd"]];
Which[f3[x] === f3[-x], Print[f3[x], " is Even"], -f3[x] === f3[-x],
Print[f3[x], " is Odd"], True, Print[f3[x], "Nor even, nor odd"]];
Remove[f, f2, f3, polyTest, polyTest2]