Optional named arguments in Mathematica

后端 未结 3 465
野性不改
野性不改 2021-02-04 10:33

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

相关标签:
3条回答
  • 2021-02-04 11:17

    Yes, OptionValue can be a bit tricky because is relies on a piece of magic so that

    OptionValue[name] is equivalent to OptionValue[f,name], where f is the head of the left-hand side of the transformation rule in which OptionValue[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.

    0 讨论(0)
  • 2021-02-04 11:24

    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]
    
    0 讨论(0)
  • 2021-02-04 11:28

    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.       *)
    
    0 讨论(0)
提交回复
热议问题