What\'s the best/canonical way to define a function with optional named arguments? To make it concrete, let\'s create a function foo
with named arguments a
Yes, OptionValue
can be a bit tricky because is relies on a piece of magic so that
OptionValue[name]
is equivalent toOptionValue[f,name]
, wheref
is the head of the left-hand side of the transformation rule in whichOptionValue[name]
appears.
Throwing in an explicit Automatic
usually does the trick, so in your case I would say that the solution is:
Options[foo] = {a -> 1, b -> 2, c -> 3};
foo[OptionsPattern[]] :=
bar @@ (OptionValue[Automatic, #] &) /@ First /@ Options[foo]
By the way, options used to be done by matching to opts:___?OptionQ
, and then finding option values manually as {a,b,c}/.Flatten[{opts}]
. The pattern check OptionQ
is still around (although not documented), but the OptionValue
approach has the advantage that you get warnings for non-existing options (e.g. foo[d->3]
). This would also be the case for your second response, but not for the one you have accepted.
I found the standard way to do it in the Mathematica documentation: http://reference.wolfram.com/mathematica/tutorial/SettingUpFunctionsWithOptionalArguments.html
Options[foo] = {a->1, b->2, c->3}; (* defaults *)
foo[OptionsPattern[]] := bar[OptionValue@a, OptionValue@b, OptionValue@c]
Typing "OptionValue" every time is a little cumbersome. For some reason you can't just make a global abbreviation like ov = OptionValue
but you can do this:
foo[OptionsPattern[]] := Module[{ov},
ov[x___] := OptionValue[x];
bar[ov@a, ov@b, ov@c]]
Or this:
With[{ov = OptionValue},
foo[OptionsPattern[]] := bar[ov@a, ov@b, ov@c]
]
Or this:
$PreRead = ReplaceAll[#, "ov" -> "OptionValue"] &;
foo[OptionsPattern[]] := bar[ov@a, ov@b, ov@c]
I'll throw this possible solution into the mix:
foo[opts___Rule] := Module[{f},
f@a = 1; (* defaults... *)
f@b = 2;
f@c = 3;
each[a_->v_, {opts}, f@a = v];
Return[bar[f@a, f@b, f@c]]
]
I like it for its terseness but I don't think it's the standard way. Any gotchas with doing it that way?
PS, it uses the following handy utility function:
SetAttributes[each, HoldAll]; (* each[pattern, list, body] *)
each[pat_, lst_, bod_] := (* converts pattern to body for *)
Scan[Replace[#, pat:>bod]&, Evaluate@lst] (* each element of list. *)