38
$\begingroup$

EDIT: As several respondents have noted in the answers and comments below, the original example had a default value that would never be used because of the way patterns and default values are applied. I've edited the example so that it now focuses on the question that was being asked and which has already been answered.

Is it possible to achieve the following behavior in a function definition:

Remove[foo];
foo[Optional[Pattern[x, _?IntegerQ], 1]] := x;
foo[]
foo[2]

1
2

using "colon syntax" shorthand?

Note that,

Remove[foo];
foo[x : _?IntegerQ : 1] := x;
foo[] 
foo[2] 

foo[]
foo[2]

does not produce the desired result.

The first code sample is too verbose; setting a default value for a function argument while simultaneously checking type when an argument is supplied should be common enough practice to deserve its own shorthand notation.

Can anyone modify the second example to achieve the desired results? If Mathematica syntax does not directly support shorthand for combining default values with argument type checking, perhaps someone could suggest how this might be achieved using the Notation package.

$\endgroup$
4
  • 4
    $\begingroup$ Note that, in your formulation, defaults will be never used (so it's not quite clear to me why bother with them then), and so in all the answers so far. In case you do want to use the default, there is a subtlety here, because the default must match the general pattern, to be used. More details are here $\endgroup$ Commented May 1, 2012 at 8:27
  • 1
    $\begingroup$ Thanks for the link and for highlighting this important subtlety regarding how pattern matching and default values work together. I edited the original question to reflect your input. $\endgroup$ Commented May 1, 2012 at 20:17
  • $\begingroup$ @R.M. In this particular case, this is important, because there are (at least) 2 different subtleties associated with this construction, and we don't want to discuss one of them using an example where another one is also broken. $\endgroup$ Commented May 1, 2012 at 20:41
  • $\begingroup$ @LeonidShifrin I agree. $\endgroup$
    – rm -rf
    Commented May 1, 2012 at 21:08

3 Answers 3

39
$\begingroup$

Perhaps this?

foo[x : (_?IntegerQ) : 1] := x;

foo[]
foo[7]
foo["string"]
1
7
foo["string"]

Update: since version 10.1 one does not need to explicitly include the default in the pattern as described below; see:

As Leonid reminds, if the default value does not match the test function it will not be returned. To allow for this you can explicitly include the value in the pattern:

ClearAll[foo]
foo[x : (_?IntegerQ | "default") : "default"] := x;

foo[]
foo[7]
foo["string"]
"default"
7
foo["string"]

In the comments magma makes an excellent point. You can use multi-clicking, or as I prefer Ctrl+. to examine parsing. See this answer.

$\endgroup$
6
  • $\begingroup$ Thanks -- I tried different parentheses because I wan't sure about the relative precedence of the colons and the underscore binding to the pattern test but missed this possible grouping. $\endgroup$ Commented May 1, 2012 at 3:27
  • 2
    $\begingroup$ In general you can use multiple clicking to see how binding works $\endgroup$
    – magma
    Commented May 1, 2012 at 8:59
  • $\begingroup$ @magma Great tip ... I was just about to ask if there was a way to expose the binding rules being used in a given expression. This will be helpful for troubleshooting future issues involving relative precedence. $\endgroup$ Commented May 1, 2012 at 19:46
  • $\begingroup$ May I ask: Why do we need the first colon? (Why not simply foo[x _?IntegerQ : 1] or foo[(x _?IntegerQ) : 1] ?) (The first alternative does not match, second one gives a syntax error, but I am interested in an conceptual reasoning.) $\endgroup$ Commented Nov 20, 2023 at 9:12
  • $\begingroup$ @user7427029 : is an overloaded operator, and it is used here both for Pattern and Optional. Without the first : you are multiplying x by something rather than naming a pattern x. $\endgroup$
    – Mr.Wizard
    Commented Feb 17 at 19:54
4
$\begingroup$

As a complement, you can also use:

Clear[foo]
foo[n:_Integer?Positive:1]:=n

That filters the foo function argument according to a Positive predicate and an Integer Head and provides in the same time a default value=1.

Examples

foo[2]

foo[2.0]

foo[]

foo[-1]

prints:

2

foo[2.]

1

foo[-1]
$\endgroup$
1
$\begingroup$

Combine Multiple Heads With Unique Pattern Test And A Default Value

The definition patQ is the magic Pattern that defies all odds. It combines three different Head-s each with its own pattern test and a default value that would not pass if given as an argument. Values that pass through produces a List: {Head, x) otherwise a Rule: "x" -> x.

ClearAll[fn, patQ]

patQ = (_Integer?Positive | _String?(StringLength@# > 1 &) | _List?(Length@# > 1 &));

fn[x : patQ : 0] := {Style[Head@x, Green], Style[InputForm@x, Green]}
fn[x_] := "x" -> InputForm@x;

All filters worked perfectly. As stated earlier, notice how this still produced a list fn[] = {Integer,0} but this didn't fn[0] = x -> 0:

fn[]

{Integer, 0}



{fn[-1], fn[0], fn[1]}

{x -> -1, x -> 0, {Integer, 1}}



{fn[""], fn["1"], fn["12"]}

{x -> "", x -> "1", {String,"12"}}



{fn[{}], fn[{1}], fn[{1, 2}]}

{x -> {}, x -> {1}, {List, {1, 2}}}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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