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 question already has an answer here:

I have a function with many of its own options, but I would also like to pass options to subfunctions using the FilterRules function. Here's a simple example:

Options[pfunc] = {"test" -> True};
pfunc[x0_, plotopts : OptionsPattern[]] := Module[{},
  Plot[x^2, {x, -x0, x0}, 
   PlotStyle -> If[OptionValue["test"], Red, Blue], 
   Evaluate[FilterRules[{plotopts}, Options[Plot]]]]]

When I just try to use the options of the pfunc it works just fine

pfunc[7]
pfunc[7, "test" -> False]

enter image description here enter image description here

But if I try to use an internal argument for Plot, it produces the figure as expected but gives error messages.

pfunc[7, "test" -> False, Frame -> True, Axes -> False]
(*
OptionValue::nodef: Unknown option Frame for pfunc. >>

OptionValue::nodef: Unknown option Axes for pfunc. >>

OptionValue::nodef: Unknown option BaseStyle for pfunc. >>

General::stop: Further output of OptionValue::nodef will be suppressed during this calculation. >>
*)

enter image description here

Why does it give these errors? Furthermore, what if I need to pass options to different subfunctions?

share|improve this question

marked as duplicate by Mr.Wizard Jun 25 at 7:07

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

1  
Change to Options[pfunc] = Join[{"test" -> True}, Options[Plot]]... –  rasher Feb 27 at 23:33
    
An extended example: (358) –  Mr.Wizard Jun 25 at 7:26

4 Answers 4

up vote 11 down vote accepted

It gives those errors because you explicitly specified that pfunc only has "test" as an option. OptionValue is finicky and will complain if it sees options that it doesn't recognize. There are a couple of alternatives that I can think of:

1: Use FilterRules everywhere instead of OptionValue

ClearAll@pfunc2
pfunc2[x0_, plotopts : OptionsPattern[]] := 
    Module[{test = If[FilterRules[{plotopts}, "test"] === {}, True]},
        Plot[x^2, {x, -x0, x0}, PlotStyle -> If[test, Red, Blue], 
            Evaluate[FilterRules[{plotopts}, Options[Plot]]]
        ]
    ]

The downside of this is that you have to pass the plotopts argument every time. You can fix this somewhat by defining some kind of local function as optval = FilterRules[{plotopts}, #]& and use that like OptionValue. However, by not explicitly specifying the options that your function takes, you make it harder for someone to get a summary of the function's usage by using ?.

2. Use a separate option to pass all options to Plot

I prefer this approach which is more verbose, but clear in intent:

ClearAll@pfunc3
Options@pfunc3 = {"test" -> True, "PlotOptions" -> {}};
pfunc3[x0_, plotopts : OptionsPattern[]] := Module[{},
  Plot[x^2, {x, -x0, x0}, 
   PlotStyle -> If[OptionValue["test"], Red, Blue], 
   Evaluate@OptionValue["PlotOptions"]]]

You can add additional checks to make sure that only valid options are being passed to Plot, but you get the idea. The added advantage is that this is easily extendable to more complex scenarios. For instance, suppose you have a type of plot that has a grid of Plot, Graphics and a DensityPlot. What do you do with the first approach if you want to pass different options to each of those? It's not going to be easy, since they all accept Frame, PlotStyle, Mesh etc. With this approach, you can group the options as "PlotOptions" -> {...}, "DensityPlotOptions" -> {...}, "GraphicsOptions" -> {...} and so on.

share|improve this answer
    
That last method is perfect, thanks very much! –  Jason B Feb 27 at 23:48

I don't know why no one mentioned this, but all you have to do is to use a special form of OptionsPattern:

pfunc[x0_, plotopts : OptionsPattern[{Plot, pfunc}]] := your-code

where inside OptionsPattern go all sub-functions you need, in a list. Now everything is fine and dandy.

This has been explained already in this answer of Mr. Wizard, so this answer and the whole question should be considered a duplicate.

There might be a downside of this, in case when several sub-functions can take the same subset of options, while you might want to pass some of the options to some sub-functions but not the other - in which case the special "PlotOptions" option, as suggested by rm -rf, might be a better solution. In the simple cases, however, OptionsPattern[{f1,f2,...}] is IMO the way to go.

share|improve this answer
    
This is a really nice way to do it, probably the simplest and most straightforward. In order for pfunc to have its own options as well, I would include it in the list of functions, i.e. pfunc[x0_, plotopts : OptionsPattern[{Plot,pfunc}]] :=. For my specific purpose, the @rm-rf allows the most precise control, but I will use this in the general case. –  Jason B Feb 28 at 17:07
    
@JasonB You don't have to include pfunc in a list of functions for its own options, it is already there implicitly. –  Leonid Shifrin Feb 28 at 17:16
    
If I take the code at the very top of the post and change 'OptionsPattern[]` to OptionsPattern[Plot], then I get the error message OptionValue::optnf: Option name "test" not found in defaults for {Plot}. But if I change it to OptionsPattern[{Plot,pfunc}] then it works just fine. –  Jason B Feb 28 at 17:48
    
Take OptionsPattern[{Plot}], as in my post. I tested it, and it worked for me. –  Leonid Shifrin Feb 28 at 18:21
    
It gives the error message for me using version 9.0.0.0, 32-bit. Unless I'm doing something wrong, here's a screenshot of the error message. –  Jason B Feb 28 at 18:28

I believe you need to add all of Plot's options to pfunc as well, like this:

Options[pfunc] = Join[ Options[Plot], {"test" -> True, ...} ]

I'd like to note that this is what builtins do as well. For example, Plot also carries all possible Graphics options. The downside is that any changes to the default options of Plot won't affect pfunc. The upside is that it will be possible to change the defaults on pfunc, and also that you get error reporting about invalid options for free.

There are some other things worth correcting: while Plot and Graphics accept options grouped into a list, as in Plot[..., {Frame -> True, PlotStyle -> Red}], many other functions don't.

So I'd write the function like this:

pfunc[x0_, plotopts : OptionsPattern[]] := 
 With[{seq = Sequence @@ FilterRules[{plotopts}, Options[Plot]]},
  Plot[x^2, {x, -x0, x0}, seq, PlotStyle -> If[OptionValue["test"], Red, Blue]]
 ]

I also moved seq before your own PlotStyle option, to make the PlotStyle overridable.

Well, this is how I usually do it, but since I don't have a lot of experience with options handling, I'm not sure it's the best way.

share|improve this answer

I have found a workaround, but I don't think it's the most elegant solution available. Basically, I separate the Plot options from the pfunc options via curly brackets, like this:

Options[pfunc] = {"test" -> True};
pfunc[{x0_, plotopts : OptionsPattern[]}, OptionsPattern[]] := 
 Module[{},
  Plot[x^2, {x, -x0, x0}, 
   PlotStyle -> If[OptionValue["test"], Red, Blue], 
   Evaluate[FilterRules[{plotopts}, Options[Plot]]]]]
pfunc[{7, Frame -> True, Axes -> False, BaseStyle -> 20}, 
 "test" -> False]

This produces the same plot as above without any error messages.

This is a fine workaround, but sometimes you want to pass options to different subfunctions. This is what I've been able to come up with, but it feels like there should be a better way:

Options[pfunc2] = {"test" -> True};
pfunc2[{x0_, 
   plotopts : OptionsPattern[]}, {plotopts2 : OptionsPattern[]}, 
  OptionsPattern[]] := Module[{p1, p2},
  p1 = Plot[x^2, {x, -x0, x0}, 
    PlotStyle -> If[OptionValue["test"], Red, Blue], 
    Evaluate[FilterRules[{plotopts}, Options[Plot]]]];
  p2 = Plot[x^3, {x, -x0, x0}, 
    PlotStyle -> If[OptionValue["test"], Red, Blue], 
    Evaluate[FilterRules[{plotopts2}, Options[Plot]]]];
  Grid[{{p1, p2}}]]


pfunc[{7, Frame -> True, Axes -> False, 
  BaseStyle -> 20}, {Axes -> False, Frame -> True, ImageSize -> 300}]

enter image description here

pfunc2[{7, Frame -> True, Axes -> False, BaseStyle -> 20}, {}, 
 "test" -> False]

And then if you don't want to pass any options you do it like this:

pfunc2[{7}, {}]

enter image description here

share|improve this answer
    
Seems weird to answer my own question, but I've been told before that this is acceptable. I have a solution, I just want to see if there is a better one, where I don't have to put in the empty curly brackets like in the last line. –  Jason B Feb 27 at 23:32

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