37

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.

user64494
  • 26,149
  • 4
  • 27
  • 56
StackExchanger
  • 1,511
  • 13
  • 20
  • 4
    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 – Leonid Shifrin May 01 '12 at 08:27
  • 1
    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. – StackExchanger May 01 '12 at 20:17
  • @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. – Leonid Shifrin May 01 '12 at 20:41
  • @LeonidShifrin I agree. – rm -rf May 01 '12 at 21:08

3 Answers3

38

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.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • 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. – StackExchanger May 01 '12 at 03:27
  • 2
    In general you can use multiple clicking to see how binding works – magma May 01 '12 at 08:59
  • @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. – StackExchanger May 01 '12 at 19:46
  • 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.) – user7427029 Nov 20 '23 at 09:12
  • @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. – Mr.Wizard Feb 17 '24 at 19:54
4

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]
Picaud Vincent
  • 2,463
  • 13
  • 20
1

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}}}

Jules Manson
  • 2,457
  • 11
  • 19