Skip to main content

function construction - SetAttributes[f,Flat]: Why the order dependence?


I'm experiencing a strange phenomenon. Here's a (quite artificial) test case:


The following definition work just fine:


f[x___Real]:={x}

g[a_,f[n_Integer]]:={a,n}
SetAttributes[f,Flat]

f[a,f[b,c]]
(*
--> f[a,b,c]
*)

g[f[1.0],3]
(*

--> g[{1.},3]
*)

As do the following (in a fresh kernel):


f[x___Real]:={x}
SetAttributes[f,Flat]
g[a_,f[n_Integer]]:={a,n}

f[a,f[b,c]]
(*

--> f[a,b,c]
*)

g[f[1.0],3]
(*
--> g[{1.},3]
*)

However, the following (evaluated in a fresh kernel again) hangs on the definition of g:


SetAttributes[f,Flat]

f[x___Real]:={x}
g[a_,f[n_Integer]]:={a,n}

Now I don't see any reason why the definition of g should depend on whether I've first defined f or first set the Flat attribute on it. Can anyone explain the mystery?


PS: If anyone can think of a better title, feel free to change it accordingly.



Answer



Ok, I'm going to try to explain my best conjuecture as to how this happens, and don't even try to answer why.


There are three reasons for this behaviour:


SetDelayed left hand side evaluation


As others have mentioned, even though SetDelayed has attributes that indicate it holds the lhs, it does evaluate the head and the arguments of it, just not the expression as a whole.



How the Attribute Flat affects the final evaluation of the expression


Ok, so the evaluator is now trying to transform the expression (with head Flat) as a whole. It has already evaluated the head and arguments, or not, as it was supposed to. It has already flattened everything. It has already tried the UpValues, nothing fit.


Now it checks, in order, one DownValue at a time:


1* If it matches the expression as is, we're good. It applies the transformation rule.


2* If it doesn't, then it modifies the expression, gathering arguments. Tries combinations of head[prev__, head[some__], aft__] with args being different subsequences of arguments taken from expression. If some subexpression fits the pattern, it transforms it and restarts the process.


Example


In[27]:= ClearAll[f];
SetAttributes[f, {Flat, HoldAll}];
f[b, c] := 8;
f[a, b, c, d]


Out[30]= f[a, 8, d]

3* If it doesn't then IT TRIES TO MATCH THE EXPRESSION head[] WITH NO ARGUMENTS. If it succeeds, then IT PREPENDS THE UNEVALUATED TRANSFORMATION OF head[] TO THE EXPRESSION AND RESTARTS THE EVALUATION PROCESS


Some examples:


In[18]:= ClearAll[f1, f2, f3, f4];
SetAttributes[{f1, f2, f3, f4}, Flat];
f1[2, 2, 2, 2] := "Yeahh";
f1[2] = "bo";
f1[] := (Print["here"]; 2);


f2[2] = "bo";
f2[2, 2, 2, 2] := "Yeahh";
f2[] := (Print["here"]; 2);

f3[] := (Print["here"]; 2);
f3[2, 2, 2, 2] := "Yeahh";
f3[2] = "bo";

f4[2, 2, 2, 2] := "Yeahh";

f4[] := (Print["here"]; 2);
f4[2] = "bo";

In[32]:= Scan[Print[DownValues[#][[All, 1]]] &, {f1, f2, f3, f4}]

During evaluation of In[32]:= {HoldPattern[f1[2,2,2,2]],HoldPattern[f1[2]],HoldPattern[f1[]]}

During evaluation of In[32]:= {HoldPattern[f2[2]],HoldPattern[f2[2,2,2,2]],HoldPattern[f2[]]}

During evaluation of In[32]:= {HoldPattern[f3[]],HoldPattern[f3[2,2,2,2]],HoldPattern[f3[2]]}


During evaluation of In[32]:= {HoldPattern[f4[2,2,2,2]],HoldPattern[f4[]],HoldPattern[f4[2]]}

In[33]:= f1[2]
f2[2]
f4[2]

Out[33]= "bo"

Out[34]= "bo"


During evaluation of In[33]:= here

During evaluation of In[33]:= here

During evaluation of In[33]:= here

Out[35]= "Yeahh"

So, in our case, when the expression f[n_Integer] fails to match the pattern f[x___Real], it evaluates f[] to get {}, and tries matching f[{}, f[n_Integer]], which doesn't match so it loops infinately.



If this is a case, then a good tip would be to always take care that all definitions of Flat symbols that match without arguments should go last...


How the order of setting attributes matter


Flat affects evaluation in 3 different moments:



  1. After evaluating the arguments of an expression, it automatically flattens it's head

  2. Changes the pattern-matching (more on this later)

  3. Changes what the evaluator does when an expression didn't match a DownValue


The 1.th and 2.th part seems to only care about Flat being set at the time of evaluation. But 3., it seems that that peculiar behaviour (see 2* and 3* above) of the evaluator trying to match the no-arguments version is PER DOWNVALUE, and it seems to me that, at the time the DownValue is set, MMA records somewhere if Flat was or not set at the time.


So, the previous f3 example that looped infinitely wouldn't loop infinitely if the no argument version was defined while the Flat attribute wasn't set.



ClearAll[f3];
f3[] := (Print["here"]; 2);
SetAttributes[{f1, f2, f3, f4}, Flat];
f3[2, 2, 2, 2] := "Yeahh";
f3[2] = "bo";

In[65]:= f3[2]

Out[65]= "bo"


The previous f4 example would also differ. You don't even need to set the Flat attribute back up in the end for the behaviour to remain


In[66]:= ClearAll[f4];
SetAttributes[f4, Flat];
f4[2, 2, 2, 2] := "Yeahh";
ClearAttributes[f4, Flat];
f4[] := (Print["here"]; 2);
SetAttributes[f4, Flat];
f4[2] = "bo";

In[73]:= f4[2]


Out[73]= "bo"

The same actually applies to (see 2*) the evaluator testing the different combinations of gathered arguments... So


In[37]:= ClearAll[f];
SetAttributes[f, {Flat, HoldAll}];
f[b, c] := 8;
ClearAttributes[f, Flat];
f[e, g] := 9;
f[a, b, c, d]

f[a, e, g, d]

Out[42]= f[a, 8, d]

Out[43]= f[a, e, g, d]

Extra for less incompleteness


Flat also affects the pattern matcher. When the pattern matcher finds itself comparing arguments of an expression whose head (let's call it head) has attribute Flat, it behaves differently: patterns of length one (_, r_, Conditions, PatternTests) trigger the pattern matcher to automatically wrap a head around the respective arguments of the expression so both the expression and the pattern have the same number of arguments (could also be a single argument, but it never leaves it as is. Don't know the purpose). In cases where more than one option is possible due to having __s as arguments, it just starts trying one way and if it doesn't match, tries the next).


In[47]:= ClearAll[f]; SetAttributes[f, {Flat, HoldAllComplete}];
Cases[Hold@f[1], Hold@f[i_] :> Hold[i], {0}]

Cases[Hold@f[1, 2, 3, 4], Hold@f[1, i_, 4] :> Hold[i], {0}]
Cases[Hold@f[1, 2, 3, 4], Hold@f[i_, __] :> Hold[i], {0}]
Cases[Hold@f[1, 2, 3, 4],
Hold@f[i_, j__ /; Length[{j}] === 2] :> Hold[i], {0}]

Out[48]= {Hold[f[1]]}

Out[49]= {Hold[f[2, 3]]}

Out[50]= {Hold[f[1]]}


Out[51]= {Hold[f[1, 2]]}

Conclusion


The problem is: Will the evaluation of f[n_Integer] in our definition of g trigger an infinite loop or not? It does evaluate because of the peculiar evaluation rules of Set functions. f[n_Integer] doesn't match the only DownValue it has at the time: f[x___Real]. In the 2 cases that work, that DownValue wasn't defined while f had the attribute Flat, so it just returns unevaluated. However, in the third case, the DownValue was defined while the symbol had Flat. So after failing, it tries to evaluate f[], returning {} and now reevaluates the whole expression as f[{}, n_Integer]


Comments

Popular posts from this blog

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Adding a thick curve to a regionplot

Suppose we have the following simple RegionPlot: f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}] Now I'm trying to change the curve defined by $y=g[x]$ into a thick black curve, while leaving all other boundaries in the plot unchanged. I've tried adding the region $y=g[x]$ and playing with the plotstyle, which didn't work, and I've tried BoundaryStyle, which changed all the boundaries in the plot. Now I'm kinda out of ideas... Any help would be appreciated! Answer With f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 You can use Epilog to add the thick line: RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}, PlotPoints -> 50, Epilog -> (Plot[g[x], {x, 0, 2}, PlotStyle -> {Black, Thick}][[1]]), PlotStyle -> {Directive[Yellow, Opacity[0.4]], Directive[Pink, Opacity[0.4]],