Skip to main content

Problem using OptionValue with functions defined by SubValues, and the use of Options for function dispatch


I have a function that looks like:


(a : pump[_, __])["curve QH", opts : OptionsPattern[]] :=
Plot[x, {x, 0, 2}, Frame -> True, FrameLabel -> {"a", "b"},

Evaluate[FilterRules[{opts}, Options[Plot]]]

This code allows me to substitute the option of Plot, with whatever I may want (where pump acts like a very simple object).


Now suppose I want to use an option to define if a Plot or a Table should be outputed.


I imagined something like this:


(a : pump[_, __])["curve QH", opts : OptionsPattern["type" -> "plot"]] :=
Which[
OptionValue["type"] == "plot",
Plot[x, {x, 0, 2}, Frame -> True, FrameLabel -> {"a", "b"},
Evaluate[FilterRules[{opts}, Options[Plot]]]],

OptionValue["type"] == "table",
Table[x, {x, 0, 2, 0.1}]
]

but it doesn't work.


What is the best strategy to use Options with this kind of definition?



Answer



There are two problems I see with this code. The first one, and the one which gives you immediate problem you reported, is that, as it looks, short (magical) form of OptionValue does not work with SubValues - based definitions. So, you should replace


OptionValue["type"]


with


OptionValue[{opts}, "type"]

Or, if you wish to associate the option with pump directly and use it within the SubValue, you need to use


OptionValue[pump, {opts}, "type"]

instead.


The second, "conceptual" problem, is that you are really trying to use options for function dispatch, but in doing this your way, you give up the powerful rule-based function dispatch mechanism that is naturally available in mma. This is what I would do instead:


ClearAll[pump, dispatch];
(a : pump[_, __])["curve QH", opts : OptionsPattern["type" -> "plot"]] :=

dispatch[pump, OptionValue[{opts}, "type"]][a, "curve QH", opts];

dispatch[pump, "plot"][_, _, opts : OptionsPattern[]] :=
Plot[x, {x, 0, 2}, Frame -> True, FrameLabel -> {"a", "b"},
Evaluate[FilterRules[{opts}, Options[Plot]]]];

dispatch[pump, "table"][_, _, opts : OptionsPattern[]] :=
Table[x, {x, 0, 2, 0.1}]

I delegated the dispatch to a new (extra) symbol dispatch, introduced specifically for this purpose. This looks cleaner and more extensible to me than using Which statement.



EDIT


Addressing the issue raised in comments: note that, as another manifestation of OptionValue - OptionsPattern magic being lost for SubValues- based functions, the above solution, as written, will not provide the intended default behavior, when the "type" option is not explicitly given. To make that work, one can do:


(a : pump[_, __])["curve QH", opts : OptionsPattern["type" -> "plot"]] :=
dispatch[
pump,
OptionValue[{opts,"type" -> "plot"}, "type"]
][a, "curve QH", opts];

adding the default option explicitly in OptionValue.


Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...

list manipulation - Selecting multiple columns from a matrix?

Sample data: data = { {{2013, 1, 1}, 24.13, 167.67, 231.82}, {{2013, 1, 2}, 32.15, 170.92, 225.99}, {{2013, 1, 3}, 35.43, 172.68, 221.67}, {{2013, 1, 4}, 36.73, 173.05, 218.32}, {{2013, 1, 5}, 58.19, 165.96, 197.05}, {{2013, 1, 6}, 69.99, 163.50, 187.52}, {{2013, 1, 7}, 71.37, 154.21, 175.58}, {{2013, 1, 8}, 72.51, 149.66, 163.25}}; I want a DateListPlot with three graphs, so for a matrix formed by columns 1 and 2, one for columns 1 and 3, and 1 for columns 1 and 4. At the moment I'm using this code: data2 = Transpose[{data[[All, 1]], data[[All, 2]]}]; data3 = Transpose[{data[[All, 1]], data[[All, 3]]}]; data4 = Transpose[{data[[All, 1]], data[[All, 4]]}]; DateListPlot[{data2, data3, data4}, Joined -> True, Filling -> {3 -> {1}}] but I have a hunch that this can be done more efficiently. I don't like the Transpose s in particular. Any ideas? edit (for extra credit) What if I need to multiply the second column by 2, which in my solution is simp...