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]] *)