Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Possible Duplicate:
How to Combine Pattern Constraints and Default Values for Function Arguments

First a simple example: define a function "add" with two arguments, and its second argument should be Positive and have a default value 1.

addv1[x_, (y_:1)?Positive] := x + y;
addv2[x_,y?Positive:1] := x + y;

these two just don't work as expected.

So is it impossible to use PatternTest and Optional value on one Pattern simultaneously, considering the probability of its default value conflicting with its pattern test?

share|improve this question

marked as duplicate by Mr.Wizard Jan 13 '13 at 15:31

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

    
Make sure you have a look at my answer. I think the answer is yes. –  Leonid Shifrin Jan 13 '13 at 15:04
2  
@LeonidShifrin Wow! I'm reading your book MathProgrammingIntro now. I have learned a lot. Thank you~ –  ywdr1987 Jan 13 '13 at 17:52
    
Good to know that it is useful, thanks. –  Leonid Shifrin Jan 13 '13 at 18:04
add comment

2 Answers 2

up vote 13 down vote accepted

The syntax

The answer is yes. I use this construct all the time. Here is the form:

add[x_, y : (_?Positive) : 1] := x + y;

You can test that it passes all the test cases.

Sutble behavior to watch out for

There is one additional subtlety associated with this construct: the default value must match the pattern specified for the explicit argument. So, for example, this definition:

Clear[addAuto];
addAuto[x_, y : (_?Positive) : Automatic] := x + y; 

won't work as expected:

addAuto[1]

(* addAuto[1] *)

because Automatic does not match _?Positive. But this will:

Clear[addAuto];
addAuto[x_, y : (_?Positive | Automatic) : Automatic] := x + y;

addAuto[1]

(* 1+Automatic *)

So, make sure that your defualt value matches the explicit arg. pattern. Many, many hours did I waste debugging such cases, more than once. It is not something that first comes to mind. See some more discussion here.

share|improve this answer
1  
Interesting... wouldn't have thought of that! I'll remove the "no" from my answer. This clearly is the winner :) –  rm -rf Jan 13 '13 at 15:07
    
@rm-rf I was thinking that this is a common place, since I personally use this all the time (in particuar, I am sure that I used this form in many of my posts here). Apparently, it seems to be not. –  Leonid Shifrin Jan 13 '13 at 15:09
    
I'm not surprised that it works though... in fact, immediately after seeing the FullForm in the OP's code, I thought: "Hmm, this might work if I simply rearranged to make Pattern the first object inside Optional" (which is what your grouping does), but then I saw the docs for the error messages and dismissed it without trying =) –  rm -rf Jan 13 '13 at 15:13
1  
@Mr.Wizard Yes, please do merge my comment into your answer, and then I will delete mine. I completely forgot about that question. –  Leonid Shifrin Jan 13 '13 at 15:55
1  
Leonid, I think you should leave this answer undeleted. I've removed my answer from the duplicate instead because the OP changed the example after I pointed it out and so my answer isn't necessary anyway. Mr.Wizard can edit his answer to clarify this subtlety, so that the original has all the bases covered. –  rm -rf Jan 13 '13 at 16:00
show 8 more comments

The two definitions you used don't work because

  • Optional cannot be used as the first argument in PatternTest, thus ruling out addv1. This is mentioned in the documentation for General::patop:

    A pattern based on Optional cannot be used as the first argument in PatternTest, Condition, Repeated, RepeatedNull, or Optional, or as the second argument in Pattern.

  • Optional requires a Blank[] as the optional object and gives a General::optb error otherwise, thus ruling out addv2

However, you can still retain the expressiveness of the single line definition that you had hoped would work by using Condition instead as:

Clear@add
add[x_, y_: 1] /; Positive@y := x + y

and it works as expected:

add[1]
(* 2 *)

add[1, 2]
(* 3 *)

add[1, a]
(* add[1, a] *)
share|improve this answer
    
Thanks for your answer and modification. –  ywdr1987 Jan 13 '13 at 14:53
add comment

Not the answer you're looking for? Browse other questions tagged or ask your own question.