I have a function which has some general behavior, but that should act on some specific kinds of objects in some other way. I know that Mathematica is supposed to automatically order the rules so that the more specific rules are applied first. Nevertheless, I want to be sure that I can change the order manually in case Mathematica is not able to do this automatically in my case.
edit: A simple example of such a situation where rule reordering takes place is (taken out of Leonid Shifrin's book, which I highly recommend)
In[1]:= Clear[f];
In[2]:= f[x_]:= Sin[x];
In[3]:= f[x_?EvenQ]:= x;
In[4]:= f[x_?OddQ]:= x^2;
In[5]:= {f[1], f[2], f[3], f[4], f[3/2], f[Newton]}
Out[1]:= {1, 2, 9, 4, Sin[3/2], Sin[Newton]}
So the Sin definition takes place last even though it was defined first. So for example in this case is there a way to make the Sin always apply first without unsetting the other definitions? Or conversely to make sure a definition is used first even though it was declared last?
Thanks, Lior
Answer
General
The definitions get reordered at definition-time by a part of the pattern matcher, that takes care of automatic rule reordering. It does so, based on relative generality of rules, as far as it is able to determine that. This is not always possible, so when it can't determine which of the two rules is more general, it appends the rules to DownValues
(or SubValues
, UpValues
, etc.) in the order the definitions are given. This is described in the documentation. Some past discussions on this site, containing more information about that, can be found here and here.
Manipualations with DownValues
As mentioned in comments and the other answer, one general way to change the order of definitions is to manipulate DownValues
directly, assigning to DownValues[f]
the rules in the order you want. This technique has been described in the documentation, and also extensively in David Wagner's book (which is available for free).
The most general way is indeed
DownValues[f] = {rules}
However, sometimes a more special form of rule rearrangement is handy: if you give a new definition, which you want to be tried first, but which you know for sure to be added last, you can do this:
f[...]:=your-new-definition;
DownValues[f] = RotateRight[DownValues[f]]
In which case, your definitions becomes the first, while all the other definitions maintain the same relative order as before. This trick has been discussed by Wagner in his book. Another example where this trick has been put to use, is here.
Using symbolic tags to fool the reordering system
This trick I haven't seen used by others, although I am sure I was not the only one to come up with it. Basically, you do something like this:
ClearAll[f, $tag];
f[x_] /; ($tag; True) := Sin[x];
f[x_?EvenQ] := x;
f[x_?OddQ] := x^2;
The pattern-matcher can no longer decide that the first rule is more general than the others, since it can't know what $tag
is, until the code runs. In practice, $tag
should have no value, and serves only to ensure that rules aren't reordered. It is also convenient since, if you no longer need such definition, you can simply do
DownValues[f] = DeleteCases[DownValues[f], def_/;!FreeQ[def, $tag]]
When it breaks
One other subtle point, that tends to be overlooked, is that definitions which don't contain patterns (underscores and other pattern-building blocks), are stored in a separate hash-table internally. In DownValues
list, they always come first - since indeed, they are always more specific than those containing patterns. And no matter how you reorder DownValues
, you can't bring those "down" the definitions list. For example:
ClearAll[ff, $tag];
ff[x_] /; ($tag; True) := Sin[x];
ff[x_?EvenQ] := x;
ff[x_?OddQ] := x^2;
ff[0] = 0;
ff[1] = 10;
Let's check now:
DownValues[ff]
(*
{HoldPattern[ff[0]] :> 0, HoldPattern[ff[1]] :> 10,
HoldPattern[ff[x_] /; ($tag; True)] :> Sin[x],
HoldPattern[ff[x_?EvenQ]] :> x, HoldPattern[ff[x_?OddQ]] :> x^2}
*)
We can attempt to reorder manually:
DownValues[ff] = DownValues[ff][[{3, 4, 5, 1, 2}]]
only to discover that this didn't work:
DownValues[ff]
(*
{HoldPattern[ff[0]] :> 0, HoldPattern[ff[1]] :> 10,
HoldPattern[ff[x_] /; ($tag; True)] :> Sin[x],
HoldPattern[ff[x_?EvenQ]] :> x, HoldPattern[ff[x_?OddQ]] :> x^2}
*)
In some sense, this is good, because e.g. this makes standard memoization idiom f[x_]:=f[x]=...
both possible and stable / robust. But this is something to keep in mind.
You can still make these definitions be the last by using the tag-trick:
ClearAll[ff, $tag, $tag1];
ff[x_] /; ($tag; True) := Sin[x];
ff[x_?EvenQ] := x;
ff[x_?OddQ] := x^2;
ff[0] /; ($tag1; True) = 0;
ff[1] /; ($tag1; True) = 10;
So that
ff[1]
(* Sin[1] *)
But then you considerably slow down the lookup for such definitions, even when they eventually fire.
Comments
Post a Comment