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.

Is it possible to create a function with optional arguments that also takes options?

Here is a simple example. I have a function f with option "g". It also has optional arguments y and z which are set to default values.

Options[f] = {"g" -> Identity};

f[x_, y_: 2, z_: 3, OptionsPattern[]] := 
 OptionValue["g"][x + y + z]

Now, if I give values for all of the arguments and an option value it works just fine.

In[3]:= f[1, 2, 3, "g" -> (#^2 &)]

Out[3]= 36

If I give only the required argument, no problems.

In[4]:= f[1]

Out[4]= 6

However, if I give the required arg and an option value but don't give the optional arguments, I run into trouble..

In[5]:= f[1, "g" -> (#^2 &)]

Out[5]= 4 + ("g" -> (#1^2 &))

Is there a good way around this?

EDIT:

Obviously, it is possible to write multiple definitions...

f[x_, y_, z_, OptionsPattern[]] := OptionValue["g"][x + y + z]

f[x_, y_, OptionsPattern[]] := OptionValue["g"][x + y + 3]

f[x_, OptionsPattern[]] := OptionValue["g"][x + 2 + 3]

I'm curious if there is a clean way to do it with a single definition.

share|improve this question
    
I tend to do the construction f[(* optional arguments *), (* required arguments *), (* options *)] myself... is there a reason you can't move optional arguments to the front? –  J. M. Feb 10 '12 at 6:25
7  
@JM when writing code for others (as I often do) it is typical to reserve the first arg for the most critical input, say the data in a statistical test and leave the remaining args for extra tweaking. Thus, the ordering is important. I typically write multiple definitions but was wondering if it is possible to do in one go. –  Andy Ross Feb 10 '12 at 6:32
    
There's also the possibility of strict typing for your optional arguments. If your optional arguments cannot have the head Rule or RuleDelayed, the typing would prevent options being interpreted as optional arguments... –  J. M. Feb 10 '12 at 6:37
2  
@JM I often use arg_?(Not[OptionQ[#]]&) to cover the Rule and RuleDelayed cases. This doesn't work though if I have optional args with default settings. –  Andy Ross Feb 10 '12 at 7:01
    
See my answer for how I believe that pattern can be used. –  Mr.Wizard Feb 10 '12 at 7:43

6 Answers 6

up vote 20 down vote accepted

Mathematica has to be able to tell that the default arguments can't be rules. So, for some special cases, you could do

Options[f] = {"g" -> Identity};

f[x_, y_Integer: 2, z_Integer: 3, OptionsPattern[]]:= OptionValue["g"][x + y + z]

Testing:

f[1, 2, 3, "g" -> (#^2 &)]
36
f[1]
6
f[1, "g" -> (#^2 &)]
36
share|improve this answer

Edit: better answer below.

I voted for Rojo's answer. If for some reason you cannot be that specific about your arguments you might use the converse:

nr = Except[_?OptionQ];

f[x_, y : nr : 2, z : nr : 3, OptionsPattern[]] := OptionValue["g"][x + y + z]

If for some further reason you need the optional arguments to be rules themselves, you could filter out specifically valid options:

Options[f] = {"g" -> Identity};

notOpts = 
  Except[Alternatives @@ Replace[Options[f], h_[a_, _] :> h[a, _], 1]];

f[x_, y : notOpts : 2, z : notOpts : 3, OptionsPattern[]] :=
  {OptionValue["g"], x, y, z}

f[1, "a" -> 7, "g" -> "g value"]
{"g value", 1, "a" -> 7, 3}
share|improve this answer
    
@MrWizard This is pretty cool, why does this work when nr = x_/;!OptionQ[x] does not? –  Andy Ross Feb 10 '12 at 7:45
    
@Andy Condition applies to the entire function pattern. Think about f[1, 2, x_ /; x > 5] := ... and f[1, 2, 3] –  Mr.Wizard Feb 10 '12 at 8:01
    
@MrWizard I guess I'm just not getting it.. its 2:00 am here so that may be why :) What I'm seeing from your example is obvious, the 3rd arg will not admit anything smaller than 5. But !OptionQ will exclude only Rule, {}, and RuleDelayed, (I think). –  Andy Ross Feb 10 '12 at 8:09
    
@Andy I mean that if the condition fails the entire definition of f does not match. –  Mr.Wizard Feb 10 '12 at 8:27
    
@Andy I was wrong. See kguler's answer below. –  Mr.Wizard Feb 10 '12 at 10:21

Here is another method that I learned through reading Inside the Mathematica Pattern Matcher:

Options[f] = {"g" -> Identity};

f[x_,
  Shortest[y_: 2, 1],
  Shortest[z_: 3, 2],
  OptionsPattern[] 
 ] := OptionValue["g"][x + y + z]

From the documentation for Shortest:

Shortest[p, pri] is given priority pri to be the shortest sequence. Matches for shortest sequences are tried first for Shortest objects with higher priorities.

share|improve this answer
    
This is maybe the most elegant answer. –  FJRA Feb 14 '12 at 5:12
    
@FJRA thank you. I thought it was worthy of its own post as it is unrelated to my first method(s). Also, welcome to the site. :-) –  Mr.Wizard Feb 14 '12 at 5:54

A bit late-to-the-party post, and complementary to the other solutions. Several answers addressed the question quite well IMO. I had my shot on a similar one here, with a solution similar to the one by @Mr.Wizard. But now I just want to stress one subtle point missed by other answers: using OptionQ will leak evaluation for functions which are HoldAll and take optional arguments and options. As an example, consider a modified solution of @Mr.Wizard:

ClearAll[f];
nr = Except[_?OptionQ];
Options[f] = {"g" -> Identity};
SetAttributes[f, HoldAll];
With[{nr = nr},
  f[x_, y : nr : 2, z : nr : 3, OptionsPattern[]] := OptionValue["g"][Hold[x + y + z]]
]

Note by the way that we had here to inject nr. In my solution in the linked Mathgroup thread, I used notOptionQ[x_] := ! OptionQ[x];, and the patterns looked like _?notOptionQ, so for this one injecting is unnecessary. In any case, try this code:

f[1, Print["*"], "g" -> 10]

You will see that printing happens as a part of the pattern-matching procedure. To avoid that, one should use this pattern instead:

nr = Except[_?(Function[elem, OptionQ[Unevaluated[elem]], HoldAll])];

Parentheses around Function are mandatory, and injecting is still required in this approach. The f redefined with this pattern does not leak evaluation:

f[1, Print["*"], "g" -> 10]

(*
  ==> 10[Hold[1 + Print["*"] + 3]]
*)

Admittedly, the case of Hold*-functions is not that often in practice, but I thought this subtlety should be mentioned here as well.

share|improve this answer
1  
As always you take it to another level. –  Mr.Wizard Feb 10 '12 at 10:12
    
@Mr.Wizard Thanks! So, back the the good old name? I must admit I got used to Mr.Wizard and the transition to Spartacus in our correspondence wasn't easy, so I am happy now. Congrats with becoming a mod, by the way! This will surely help make this site a better place. –  Leonid Shifrin Feb 10 '12 at 10:20
    
Thank you Leonid. I always intended to switch back. –  Mr.Wizard Feb 10 '12 at 10:24

Here's another possibility that makes use of a more involved pattern to delimit the arguments from the options. It seems to me that people writing "function definitions" are inclined to think of them rigidly in that way, commonly forgetting that these are still just patterns and can be used (and abused) as such. However, bearing in mind the true nature of these definitions can be useful in circumstances like this:

Options[f] = {"g" -> Identity};

Module[{argumentDelimiter},
 SetAttributes[argumentDelimiter, HoldAllComplete]; (* thanks Leonid! *)
 f[args : Shortest[___], opts : OptionsPattern[]] := 
  f[argumentDelimiter[args], opts];
 f[argumentDelimiter[x_: 1, y_: 2, z_: 3], OptionsPattern[]] := 
  OptionValue["g"][x + y + z];
];

(As you can see, I made the first argument optional as well for didactic purposes.)

It seems to work well enough:

In[3] := f[]

Out[3] = 6

In[4] := f["g" -> Internal`Square] (* ;) *)

Out[4] = 36

In[5] := f[3, 3, "g" -> Sqrt]

Out[5] = 3

In[6] := f[1, 17, "pianoforte" -> "harpsichord", "g" -> Sqrt]
         OptionValue::nodef: "Unknown option pianoforte for f." >>

Out[6] = Sqrt[21]

In fact, I wasn't able to trip this method up at all, which makes me slightly suspicious that I didn't try enough test cases. Can anyone else point out some shortcomings of this approach?

Edit

In response @Mr.Wizard's comments, I thought it best to clarify the following:

Why use argumentDelimiter, rather than Hold or HoldComplete?

Because argumentDelimiter is created inside a Module, its uniqueness can be guaranteed. As a result there is no possibility for conflict when one wishes to define functions that accept arguments wrapped with Hold etc. This approach is commonly seen in packages, although in that case a suitable symbol will usually be created in the package`Private` context once and for all rather than relying on Module because of the associated overheads.

Isn't Shortest redundant?

In principle, yes; however, since OptionsPattern[] may also make use of Shortest, in my opinion it is better to avoid unexpected conflicts by explicitly requiring the shortest possible sequence of arguments. As documented for Shortest, in case of two competing Shortest patterns appearing in the same expression, the one that appears first has higher priority, so anything that can match OptionsPattern[] always will in this case.

What if the optional but non-option arguments also happen to match OptionsPattern[]?

In this case one has problems regardless of using this technique, and I would consider it to be a limitation of OptionsPattern[] that invalid options also match. To avoid this situation, one must use @Mr.Wizard's suggestion instead of OptionsPattern[], i.e.:

validOptionsPattern[f_] := 
  Alternatives @@ 
    Replace[Options[f], (h : (Rule | RuleDelayed))[opt_, _] :> h[opt, _], 1] ...;

The approach then becomes:

Module[{argumentDelimiter}, 
 SetAttributes[argumentDelimiter, HoldAllComplete];
 f[args : Shortest[Except[_argumentDelimiter] ...], opts : validOptionsPattern[f]] :=
  f[argumentDelimiter[args], opts];
 f[argumentDelimiter[x_: 1, y_: 2, z_: 3], OptionsPattern[]] := 
  OptionValue["g"][x + y + z];
];

Note that Options[f] must be set before evaluating this definition so that validOptionsPattern[f] has the correct value. (At least for the time being, because making a validOptionsPattern[] that works exactly like OptionsPattern[] except for validating the options seems to be non-trivial.)

So, first with Options[f] = {"g" -> Identity}:

In[5] := f[1, 17, "pianoforte" -> "harpsichord", "g" -> Sqrt]

Out[5] = Sqrt[18 + ("pianoforte" -> "harpsichord")] (* treated as argument *)

And now with Options[f] = {"g" -> Identity, "clarinet" -> "vuvuzela"}:

In[10] := f[1, 17, "clarinet" -> "vuvuzela", "g" -> Sqrt]

Out[10] = Sqrt[21] (* option accepted *)

So everything appears to work correctly. However, if your optional arguments could also be interpreted as valid option values, I would consider that situation ill-defined and as such outside the scope of this answer.

share|improve this answer
    
+1. I would add however that depending on one's perspective, your own In[6] := lines shows this "tripping up" which is why I provided the methods that I did. –  Mr.Wizard Feb 12 '12 at 5:30
    
Also, what is the reason for using argumentDelimiter rather than Hold or HoldComplete? –  Mr.Wizard Feb 12 '12 at 5:36
1  
@MrWizard In principle Shortest should be redundant but I don't know if OptionsPattern[] makes use of Shortest, so per the documentation of Shortest (first Shortest wins) I used it anyway. And I used a unique symbol to avoid conflicts with functions that take Hold/HoldComplete as part of their normal arguments. –  Oleksandr R. Feb 12 '12 at 5:43
1  
I agree that this is normal behavior. I was simply looking at the corner case where one of the named arguments should be in the form of a rule, as you asked us to point out shortcomings and it's the only one I can see. –  Mr.Wizard Feb 12 '12 at 5:44
2  
Good stuff - +1. I often use Hold or HoldComplete to simply separate options from arguments, like f[Hold[x_,y_,x_],opts:OptionsPattern]. This allows me to not make f HoldAll (assuming I want to hold x,y and z). The advantage is that I can call it as f[Hold[a,b,c],opts] and this will match if opts stores some options. Otherwise, I'd have to make f HoldAll and call it as f[a,b,c,Evaluate[opts], and in practice it is very easy to forget Evaluate and get bugs, particularly if I expose the function to the user, who can not be expected to remember about Evaluate. –  Leonid Shifrin Feb 12 '12 at 10:41

I too like Rojo's answer. And Mr. Wizard's answer is intriguing as always. A third possibility is adding the condition that the Heads of second and third arguments are not Rule after the LHS. So, the following also works

 Options[f] = {"g" -> Identity};
 f[x_, y_: 2, z_: 3,  OptionsPattern[]] /; (Head[y] =!= Rule && Head[z] =!= Rule):= 
 OptionValue["g"][x + y + z]

The condition could be alternatively stated as (OptionQ[y] =!= True && OptionQ[z] =!= True).

share|improve this answer
    
@Mr.Wizard, hope the new condition works? –  kguler Feb 10 '12 at 9:53
    
I didn't think that would work. I like being proven wrong: it's embarrassing but I learn from it. +1 –  Mr.Wizard Feb 10 '12 at 10:17

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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