What is the recommended way to define numeric function with special cases?

What is the recommended way to define a purely numeric function with special cases? Should I define many special cases with pattern matching?

(*TOY EXAMPLE*)
f[a_,b_]/;a>b := Sin[a-b];
f[a_,b_]/;a<b := Tan[a/b];
f[a_,b_]/;a==b := Cos[a+b];


or should I use Piecewise?

f[a_,b_] := Piecewise[{{Sin[a-b],a>b},{Tan[a/b],a<b},{Cos[a+b],a==b}}]


or should I use UnitStep? How about If statements?

Also What about if the function is continuous across each segments (not like the toy example above)? and if the function is smooth?

• I gave a brief answer below with my own opinions. Later I shall try to add examples to support my statements if someone has not already provided a better answer by that time. Commented Sep 28, 2014 at 17:05
• Use PieceWise, UnitStep and friends, if you want your function to integrate well with mathematical functionality such as simplifications, etc. Otherwise use rules. The If is inferior to both of those ways, in most cases. Commented Sep 28, 2014 at 17:06
• @Leonid I forgot about that benefit of Piecewise. Commented Sep 28, 2014 at 17:19
• @Jens Two reasons: it makes the code harder to extend (and actually, harder to read) and more error-prone when there are several branches (you would need nested If or Which), and also it tends to be somewhat slower (not by much). But, I'd agree that this is largely a matter of taste. Philosophically, the closer you are to the core language constructs, the better, and patterns / rules are certainly closer to the core than the If/Which statements, because the core of Mathematica is a term-rewriting engine. Commented Sep 28, 2014 at 18:07
• If/Which and conditional pattern methods will not be handled well by either the algebra (Solve family) or calculus (continuous and discrete) functions. Commented Sep 28, 2014 at 21:20

From testing in version 7, which I have not yet repeated in version 10, I recommend that you use your first form, as I found it to have at least a small performance advantage over Piecewise etc. I also find it very readable.

If you can reformulate your function for application to vectors then the use of UnitStep etc., where possible without being overly contrived, may be considerably faster. I see that as a special optimization technique however, not a matter of standard practice.

One place that Piecewise, Switch etc. can be easier to use is if you wish to programmatically manipulate the function.

Regarding Also I think I need an example of what you mean by "continuous across each segments."

In general, I'd also use Piecewise because it's clearest.

However, to play devil's advocate, here is an example of where Piecewise is not the best choice (at least in Mathematica version 10.0.0):

Plot[
Piecewise[{{Sin[x], {x, 0} ∈
ImplicitRegion[-1 < x < 1, {x, y}]}, {1, True}}], {x, -2, 2}]


The warning here seems to be due to the fact that Piecewise holds all its arguments and then analyzes the conditions in a form that has not been fully evaluated. At that stage it doesn't recognize the new region functionality and spits out the warning. Fortunately, the rest of the calculation is correct, but clearly the warning is incorrect. This is most likely a bug in Plot (and appears to have been fixed in version 10.0.1 on Mac as per Michael's comment below), and it can be circumvented by using If because the condition in If is not held unevaluated:

Plot[
If[{x, 0} ∈ ImplicitRegion[-1 < x < 1, {x, y}], Sin[x],
1], {x, -2, 2}]


With this you get the same plot but no warnings.

Here the difference is that If has attribute HoldRest whereas Piecewise has attribute HoldAll, which we don't want.

Assuming this bug will get fixed, the fact remains that the different Hold... attributes of If may in certain cases make it the more natural choice, compared to Piecewise. If that happens, the choice would be more between If and pattern-based alternatives. Then the decision could still depend on other details, such as whether you intend to Compile the function.

• The first example, i.sstatic.net/LUovE.png, works in V10.0.1, Mac OS 10.9.5. Commented Sep 29, 2014 at 16:44
• @MichaelE2. Interesting. The plot generated also looks different. Commented Sep 29, 2014 at 16:49
• @MichaelE2 Thanks, I added that info.
– Jens
Commented Sep 29, 2014 at 16:53
• Actually, it is not fixed in Windows. I'm also using V10.0.1. Commented Sep 29, 2014 at 16:54
• @RunnyKine OK thanks, I edited that in as well...
– Jens
Commented Sep 29, 2014 at 16:58

Option 1 : Condition

f[a_,b_]/;a>b := Sin[a-b];
f[a_,b_]/;a<b := Tan[a/b];
f[a_,b_]/;a==b := Cos[a+b];


Option 2 : Piecewise

f[a_,b_] :=
Piecewise[{
{Sin[a-b], a>b},
{Tan[a/b], a<b},
{Cos[a+b], a==b}
}]


Option 3 : Which

Which[
a>b,Sin[a-b],
a<b,Tan[a/b]
a==b,Cos[a+b]
]


Option 4 : If

If[a>b,
Sin[a-b],
IF[a<b,Tan[a/b], Cos[a+b]]
]


I left out options Pattern test(?), Switch, Boole and others that are derived from them, like Cases, Except...

As personal preference I tend to use option 2 and Boole. If you ask why Boole then I would answer :

 PiecewiseExpand[Boole[a]]


There are more then 1 reason I do this, but mostly they are named in manual under details.