5
$\begingroup$

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.

$\endgroup$
  • 3
    $\begingroup$ Just add a module variable fail=False, change the Return[$Failed] to fail=True, after all checks, add If[fail,Return[$Failed]]. $\endgroup$ – ciao Jun 28 '15 at 7:01
  • $\begingroup$ What is your reason for preferring method/.{opts}/.Options[func] over OptionValue[method]? $\endgroup$ – Mr.Wizard Jun 28 '15 at 7:03
  • $\begingroup$ 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? $\endgroup$ – Mr.Wizard Jun 28 '15 at 7:05
  • 5
    $\begingroup$ 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) $\endgroup$ – Mr.Wizard Jun 28 '15 at 7:16
  • $\begingroup$ 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." $\endgroup$ – Mr.Wizard Jun 28 '15 at 7:28
5
$\begingroup$

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.

$\endgroup$
  • $\begingroup$ That's a good way - perhaps add the fact default is restored to msg. +1 $\endgroup$ – ciao Jun 28 '15 at 7:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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