Explanation
The problem is that pattern matching in Mathematica is purely structural - this means that e.g. the pattern a_ + b_ * f[_]
will not match 3
(even though you could set b
to 0
from a mathematical point of view):
split = a_ + b_*f[_] :> {a, b};
1 + 3 f[x] /. split
3 /. split
(* {1, 3} *)
(* 3 *)
To circumvent this, we can use Optional
to indicate that a given part of the pattern can be omitted:
split = a_ + HoldPattern@Optional[b_*f[_], 0 f[_]] :> {a, b};
1 + 3 f[x] /. split
3 /. split
(* {1, 3} *)
(* {3, 0} *)
Note the use of HoldPattern
to prevent 0 f[_]
from evaluating to 0
. You might ask "why do we even need the second argument of Optional
? After all, Plus
has a global Default
value of 0
set." However, this does not work:
split = a_ + Optional[b_*f[_]] :> {a, b};
1 + 3 f[x] /. split
3 /. split
(* {1, 3} *)
(* 3 *)
As far as I can tell, this is because the default value 0
does not match the pattern b_*f[_]
, which prevents Optional
from working, as noted by @LeonidShifrin here:
This feature of the pattern-matcher is not very widely known, so I
will stress it again: the default value for the (optional) pattern
x:ptrn:default
must match ptrn
. For another example of such behavior,
see this Mathgroup discussion.
Now there was some change regarding this, but it does not appear to affect this particular case.
We are also using the (undocumented?) fact that Optional
will set the values of omitted pattern variables according to the default value:
{} /. {Optional[{a_, f[b_]}, {aDef, f[bDef]}]} :> {a, b}
(* {aDef, bDef} *)
Summary
- We need an explicit default value for
Optional
because the pattern b_ f[_]
does not match the global default 0
- We need HoldPattern to prevent
0 f[_]
from evaluating
- The value of
b_
is set to 0
in case the default of the Optional
is used. This is done by matching b_ f[_]
to the default 0 f[_]
. (So if the default were to be 3 f[_]
, b
would be 3
)
Example of question
Armed with this knowledge, it becomes rather1 straightforward to fix the example of the question:
split = (
a_ +
HoldPattern@Optional[b_*E^x_, 0*E^_] +
HoldPattern@Optional[c_*Cos[y_], 0*Cos[_]] +
HoldPattern@Optional[d_*Sin[z_], 0*Sin[_]] :>
{a, b, c, d}
);
exp1 /. split
exp2 /. split
exp3 /. split
(* {(x y z)/(x + y + z^2), x y z, x/(y + z), (x z)/(y + z)} *)
(* {(x y z)/(x + y + z^2), x y z, 0, 0} *)
(* {(x y z)/(x + y + z^2), 0, x/(y + z), (x z)/(y + z)} *)
1 There is one small catch: We have to replace Exp[_]
by E^_
, since we're using HoldPattern
, so Exp[_]
no longer evaluates to E^_
:
Exp[3] // InputForm
(* E^3 *)
E^3 // InputForm
(* E^3 *)
HoldPattern[Exp[3]] // InputForm
(* HoldPattern[Exp[3]] *)