# How can I use nested conditionals inside my functions?

I'm a big fan of using conditionals inside my functions to deal with cases, that is, to pattern match in stages and possibly leave a function unevaluated.

In[1]:= f[x_] :=
(
(
(
True
) /; EvenQ[x]
) /; x > 2
) /; x < 10

In[2]:= f[4]
Out[2]= True


However I don't fully understand the extent to which I can do this. For example, mixing If statements in doesn't work:

In[3]:=
h[x_] :=
(
If[x > 2,
(
True
) /; EvenQ[x]
,
False
]
) /; x < 10

In[4]:= h[4]
Out[4]= True /; EvenQ[4]


A clarification on both the limitation and merits of this style of coding would be helpful.

I find this style of programming to be handy for a number of reasons, but a primary reason is the ease of defining a blanket default case and letting the pattern matching do its work, e.g. f[_] := $Failed. - The reason the second breaks is because something like (5/;True) doesn't evaluate to 5 (such as If[] would have). The outermost pattern is used to check when the function definition applies, but the innermost is just a returned pattern. As for the first function, I would personally prefer to write it as f[x_ /; (EvenQ[x] && 2 < x < 10)] := True. – jVincent Jun 14 '12 at 23:24 A related stylistic question is Placement of Condition /; expressions – Jens Jun 14 '12 at 23:54 Conditions are really always parts of patterns and replacement rules, and in isolation they are just symbolic expressions with no semantics. Once you mix patterns and non-pattern code (If-s etc), you are likely to generate such situations. Have a look here for a relevant discussion. – Leonid Shifrin Jun 15 '12 at 8:11 Regarding your update, you can also get f[_] :=$Failed behavior using And; also, you still have not described the output you desire from h. – Mr.Wizard Jun 16 '12 at 0:02

## 5 Answers

I think the general notion here is that conditional patterns x_/;EvenQ[x] and conditional expressions If[EvenQ[x],a,b] are not the same thing. In your first example, you nest conditional patterns in the definition of f which means that all the conditions have to be true for a match against the pattern to be successful. In the second, only the outermost condition is actually used in the definition of h, and it assumes you want your returned expression from the True case of If[x>2,...] to return a conditional pattern when x>2. which is what you get, the conditional pattern True /; EvenQ[4]. This pattern matches against True, so MatchQ[True, True /; EvenQ[4]] would return True.

If I understand your intention correctly the second function would be defined as:

 h[x_ /; (EvenQ[x] && x < 10)] := If[x > 2, True, False]


Note that I have kept all the conditional patterns on the left, and return a conditional expression, which evaluates depending on x.

-
+1 for fast response and making h more like f. – Jens Jun 14 '12 at 23:56

It's perhaps lucky that If has attribute HoldRest, so that it shows us directly what's going on in your definition of h.

I just saw jVincent's comment as I typed this, so I'll just add that you can mix in the If statement as follows:

h[x_] := (If[x > 2, x /. (y_ /; EvenQ[y] :> True), False]) /; x < 10


The condition has to be preceded by a pattern, and here I used y_ as the named pattern in the innermost rule before applying that rule to x using x /. (y_ ...).

-
With this definition h[3] returns 3, since it doesn't match the EvenQ[y] pattern. However if I correctly interpreted mikes intentions for the function, it should return unevaluated, since it doesn't match his nested pattern. – jVincent Jun 14 '12 at 23:46
@jVincent I just took the definition of h he provided and made it work. It's not the same as f, but I didn't think that was the main point. – Jens Jun 14 '12 at 23:51
Indeed, and your solution shows how to go about using conditional patterns in the return of the function defining pattern. I wanted to point out that the syntax doesn't "elevate" the nested pattern to something which affects the pattern used to match for the function definition. – jVincent Jun 14 '12 at 23:58

If you like conditionals and spreading definitions (which I like too), this is how you would do what you wanted (assuming jVincent understood correctly your intentions)

h[x_] := haux[x] /; EvenQ[x] /; x < 10
haux[x_] := True /; x > 2
haux[_] := False;


The extra symbol can always be localized. Or you can avoid it if you don't mind using other constructs, such as

h[x_] := TrueQ[x>2]/; EvenQ[x] /; x < 10


This is probably how I would do it myself. But the first example is probably how I would deal with a longer definition, where the advantages of spreading the concerns are bigger

-

The built in function Piecewise is designed to achieve this exact goal:

f[x_] := Piecewise[{{True, x > 2 && x < 10 && EvenQ@x}}, x]

-
+1 very efficient! – R Hall Jun 15 '12 at 22:52

As has been shown, but not explicitly stated, And tests a series of conditions in sequence and short-circuits to False if any of them fail. Therefore your first example f could be written:

f[x_ /; x < 10 && x > 2 && EvenQ[x]] := True


Notice the order of the tests; your code as written tests outside in, so this is equivalent.

I find your second example, h, confusing in terms of expected output. The complications are:

1. when you expect the function to evaluate and when you don't.

2. what you expect (True) /; EvenQ[1] or (True) /; EvenQ[2] to do.

One interpretation is that you want h to evaluate if x < 10:

ClearAll[h]

h[x_ /; x < 10] := x > 2 && EvenQ[x]

h /@ {5, 6, 15}

{False, False, True, h[15]}


Another interpretation is that you do not want h to evaluate unless multiple conditions are met and you want to base these conditions on code that is evaluated in the body of the function. In that case you are looking for this which I described briefly in the middle of this answer.

-
Yes, I "do not want h to evaluate unless multiple conditions are met". But I just want to know is it more or less efficient to use this programming construct, or is the difference negligible? – M.R. Jun 15 '12 at 19:51
@Mike the form with And is marginally more efficient. I still don't understand why you favor the nested Condition form; I wish you would explain its purpose. Also, I don't know what output you desire from h. Would you please give the (fake) output you want from h /@ Range[0, 12]? – Mr.Wizard Jun 15 '12 at 21:36