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.

This is kind of a follow-up to this question.

I want to be able to define a function that takes an option, and then, inside the function, retrieve and test the unevaluated value of the option. For example, I'd like to be able to define the following (this is obviously a silly example, but nonetheless): a function that accepts an expression and an option, Power, that should be allowed the values one and two. It should plot the appropriate power (first or second) of the given expression. So one way of doing this is

SetAttributes[plotIt, HoldAll];
Options[plotIt] = {Power -> one};
plotIt[f_, OptionsPattern[]] := Module[{},
   Switch[OptionValue[Power],
    one, Plot[f, {x, 3, 5}],
    two, Plot[(f)^2, {x, 3, 5}]
    ]
   ];

which works just fine, except that what is being tested is the evaluated one and two. Thus if those are defined variables in the current environment that happen to have the same value, invocations of this function will always use the first branch of the switch statement.

I've tried various ways to extract the name of the option value, without success. And even if I do so, I'm not sure how to refer to it in the various branches of the switch statement.

Thanks.

share|improve this question
2  
I think Options[plotIt] = {Power -> Left}; is meant to be Options[plotIt] = {Power -> one};. Anyway I would just use strings and make my life immeasurably simpler. So instead of one you can use "one". A lot of the built in options have started using strings. An example. –  Ajasja Aug 1 '12 at 14:05
1  
Note that Power already has a meaning in Mathematica. It is generally best to choose symbol names which are not in use by the system. –  Simon Woods Aug 1 '12 at 14:30

3 Answers 3

up vote 1 down vote accepted

Actually, the question can be answered without making any compromises if you realize that an OptionsPattern is ultimately just a pattern. So all you have to do is treat it as such:

SetAttributes[plotIt, HoldAll];
plotIt[f_, opts___] := Module[{},
   Switch[
    First@Append[
      Cases[Hold[opts],
       Rule[Power, y_] :> Hold[y], Infinity
       ],
      Hold@one
      ],
    Hold@one,
    Plot[f, {x, 3, 5}],
    Hold@two,
    Plot[(f)^2, {x, 3, 5}]
    , _, "Nothing to do"
    ]
   ];

one = 1; two = 1;
plotIt[-x, Power -> one]

This produces the desired plots without using strings and without using :> in the options. I just had to avoid using the shortcut command OptionsPattern and select the rule I want from the Hold[opts] directly using Cases. The First makes sure we use the first value if a sequence of options is given.

And of course the HoldAll attribute is also still used (added above for completeness).

Edit

The literal use of symbols like one in defining the default options is a separate problem, and here I would say it's best to avoid using Options[plotIt] = {Power -> one} because the one isn't protected from being replaced by its global assignment. Therefore, I would suggest to define the defaults for options inside the function itself.

That's what I did above, by Append-ing the choice Hold[one] to the list produced by Cases from the argument opts. So if opts contains no setting for Power, the appended value will automatically be found by First, and that's the value we will end up with for the plots.

So with this default option defined inside the function, we can also give the simpler command

plotIt[-x]

and get the result appropriate for Power -> one.

share|improve this answer
1  
This has the same "problem" that the method in my answer does, in that defining option and using that doesn't work, because it doesn't evaluate before the pattern matching. This is simply the nature of HoldAll. One would have to use a generic pattern and do evaluation inside the function to get around this, but I do not think it is worth it or advisable. –  Mr.Wizard Aug 1 '12 at 18:29
    
@Mr.Wizard You're right of course - but default options are very easy to handle in my approach, see the edited answer. It just doesn't use the simple Options setting. –  Jens Aug 1 '12 at 19:24

Ajajsa's advice to use strings is the simplest solution, but here's a workaround anyway. The option value is extracted wrapped in Hold, and the possible settings are also wrapped in Hold in the Switch expression.

plotIt[f_, OptionsPattern[]] := Module[{}, Switch[
    OptionValue[Automatic, Automatic, Power, Hold],
    Hold@one, Plot[f, {x, 3, 5}],
    Hold@two, Plot[(f)^2, {x, 3, 5}]]];

It is necessary to use RuleDelayed when giving the option:

one = 1; two = 1;
plotIt[-x, Power :> two]

enter image description here

share|improve this answer

This has been asked and answered on StackOverflow.

There are choices to be made. It is probably best to use RuleDelayed as Leonid there, and Simon here show, especially for your own functions. If one forgets and uses Rule instead however behavior changes. Pillsy shows how to make Rule work too:

ClearAll[f]
SetAttributes[f, HoldAll];
Options[f] = {op1 -> None, op2 -> None};

f[___, opts : OptionsPattern[]] := 
  OptionValue[f, Unevaluated[{opts}], op1, Hold]

f[x, y, z, op1 -> 1 + 2 + 3]
Hold[1 + 2 + 3]

The problem with this, as Leonid points out, is that Hold attributes prevent this usage:

f[___, opts : OptionsPattern[]] := OptionValue[op2]

option = op2 -> 5;

f[x, y, z, option]
None

(Without HoldAll the output here would be 5.)

share|improve this answer

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.