5

Given that I have a function func as follow:

To check the value of options, I using the step-by-step method If[]

Options[func] = {Method -> Automatic, WorkingPrecision -> MachinePrecision, Order->2};

func::badval = "`1` is not a valid value of option `2`";

func[arg1_,arg2_, opts : OptionsPattern[]] := 
 Module[{method, prec, order},
  method = Method /. {opts} /. Options[func];
  prec= WorkingPrecision/. {opts} /. Options[func];
  order= Order/. {opts} /. Options[func];
  (*Check the value of the options*)
  If[
   ! MemberQ[
    {{"Euler", 2}, {"Midpoint", 2}, {"Heun", 2}, {"Heun", 3},
     {"Kutta", 3}, {"RK", 4}, {"Gill", 4}, Automatic}, method],
    Message[func::badval, method, Method]; Return[$Failed]];

  If[
   ! (prec === MachinePrecision || NumericQ[prec]),
   Message[func::badval, prec, WorkingPrecision]; Return[$Failed]];

  If[
   ! (Head[order] ===Integer&& order >= 1),
   Message[func::badval, order, Order]; Return[$Failed]];

  (*main implementation*)
  arg1 + arg2
 ]

Test

func[1, 2, WorkingPrecision -> MachinePrecisio, Order -> 1., Method -> Automati]

func::badval: Automati is not a valid value of option Method

However, I hope the warning information should like this or like the Plot(see below)

func::badval: Automati is not a valid value of option Method

func::badval: MachinePrecisiois not a valid value of option WorkingPrecision

func::badval: 1.is not a valid value of option Order

The built-in throw the warning information

Plot[2 Sin[x] + x, {x, 0, 15}, 
 PlotRange -> {a, c}, Filling -> bb, 
 Mesh -> pp, Axes -> Fals, WorkingPrecision -> MachinePrecisi]

General::prng: Value of option PlotRange -> {a,c} is not All, Full, Automatic, a positive machine number, or an appropriate list of range specifications.

Plot::wprec: Value of option WorkingPrecision -> MachinePrecisi is not a positive machine-sized real or integer. >>

Mesh::ilevels: pp is not a valid mesh specification. >>

Question

  • How to check the value of options like the built-in functions, especially when the function have many options(the option number of Plot is 60)?

As said by Mr.Wizard in the comment, I hope that the func executes the following operation:

(1) Multiple messages are issued

(2) Using default option values.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
xyz
  • 605
  • 4
  • 38
  • 117
  • 3
    Just add a module variable fail=False, change the Return[$Failed] to fail=True, after all checks, add If[fail,Return[$Failed]]. – ciao Jun 28 '15 at 07:01
  • What is your reason for preferring method/.{opts}/.Options[func] over OptionValue[method]? – Mr.Wizard Jun 28 '15 at 07:03
  • In your Plot example (1) multiple messages are issued and (2) the plot is still rendered using default option values. Are both these behaviors something you wish to duplicate? – Mr.Wizard Jun 28 '15 at 07:05
  • 5
    Okay. That is an old manual, and while it is still an authoritative reference for its time there are newer methods available. This is such a case. OptionValue is now the preferred method in most cases and I intend to use it where applicable. I suggest you read: (355) – Mr.Wizard Jun 28 '15 at 07:16
  • I am waiting for you to answer my question about the behaviors you wish to emulate before I start on an answer. Better yet update your question to include a specific description of what you mean by "like the built-in functions." – Mr.Wizard Jun 28 '15 at 07:28
  • Okay. Again it would be best if you include that in your Question to avoid ambiguity. – Mr.Wizard Jun 28 '15 at 07:33

1 Answers1

5

I believe the simplest change to your code is to replace the Return[$Failed] expressions with Sets that restore the default values of the options:

ClearAll[func]

Options[func] = {Method -> Automatic, WorkingPrecision -> MachinePrecision, Order -> 2};

func::badval = "`1` is not a valid value of option `2`";

func[arg1_, arg2_, opts : OptionsPattern[]] :=
 Module[{method, prec, order},
  method = OptionValue[Method];
  prec = OptionValue[WorkingPrecision];
  order = OptionValue[Order];
  (*Check the value of the options*)
  If[! MemberQ[{{"Euler", 2}, {"Midpoint", 2}, {"Heun", 2}, {"Heun", 3}, {"Kutta", 
       3}, {"RK", 4}, {"Gill", 4}, Automatic}, method], 
   Message[func::badval, method, Method]; method = Method /. Options[func]];
  If[! (prec === MachinePrecision || NumericQ[prec]), 
   Message[func::badval, prec, WorkingPrecision]; 
   prec = WorkingPrecision /. Options[func]];
  If[! (Head[order] === Integer && order >= 1), Message[func::badval, order, Order]; 
   order = Order /. Options[func]];
  (*main implementation*){arg1 + arg2, {method, prec, order}}]

Now:

func[1, 2, WorkingPrecision -> MachinePrecisio, Order -> 1., Method -> Automati]

func::badval: Automati is not a valid value of option Method

func::badval: MachinePrecisio is not a valid value of option WorkingPrecision

func::badval: 1.` is not a valid value of option Order

General::stop: Further output of func::badval will be suppressed during this calculation. >>

{3, {Automatic, MachinePrecision, 2}}

(Note that I included the option values explicitly in the output for visibility.)

This does make the assumption that the Options settings are never wrong. A more robust method would be to keep a separate set of known safe option values as hidden defaults, e.g.:

$defaultOptions[func] =
   {Method -> Automatic, WorkingPrecision -> MachinePrecision, Order -> 2};

Then in the function body use:

Method /. $defaultOptions[func]

This will protect against an invalid SetOptions[func, . . .] usage as well.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371