MathGroup Archive 2010

[Date Index] [Thread Index] [Author Index]

Search the Archive

Re: locally changing Options

  • To: mathgroup at smc.vnet.net
  • Subject: [mg108261] Re: [mg108224] locally changing Options
  • From: Leonid Shifrin <lshifr at gmail.com>
  • Date: Fri, 12 Mar 2010 07:08:46 -0500 (EST)
  • References: <201003111134.GAA05909@smc.vnet.net>

Hi Ralf,

To my knowledge, there is no built-in functionality that would immediately
solve your problem. One way is to automate the steps you mentioned, for
example like this:


ClearAll[withOptions];
SetAttributes[withOptions, HoldFirst];
withOptions[code_, f_, opts : {__?OptionQ}] :=
  Module[{old = Options[f], result},
   SetOptions[f, opts];
   result = code;
   SetOptions[f, old];
   result];


Here is a test function for an illustration:

ClearAll[fn];
Options[fn] = {Display -> False};
fn[x_, y_, opts___?OptionQ] :=
  If[TrueQ[Display /. Flatten[{opts}] /. Options[fn]],
   Print[x, "   ", y]; x + y,
   x + y];

and this is some function which stands for the entire code you want to
execute with a different option setting:

ClearAll[myFunction];
myFunction[] := fn[1, 2]^2;

Here is how you can use it:

In[868]:= myFunction[]

Out[868]= 9

In[869]:= withOptions[myFunction[], fn, {Display -> True}]

During evaluation of In[869]:= 1   2

Out[869]= 9


The advantage of this approach is that it is simple and straightforward. The
disadvantage however is not even that this "looks ugly", since we automated
these steps, but that if an exception is thrown or Abort[] happens in the
middle of your code, you end up with your option not changed back to what it
was. This can be dealt with in this approach, but it will become more
complex.

There is another approach that I use sometimes, based on dynamic scoping and
local option passing. I wrote a tiny package which allows .to "subscribe"
any given function to be a receiver of some "global" options. Here it is:

(* ::Package::*)

BeginPackage["OptionPipe`"];


OptionPipe::usage =
  "OptionPipe is a wrapper that allows to propagate options
  non-locally";

OptionsInPipe::usage =
  "OptionsInPipe[f_] returns options currently in OptionPipe";

SubscribeToPipe::usage =
  "SubscribeToPipe[fun_] makes the options present in pipe
  at the moment of the call, to be passed to <fun>";

UnsubscribeFromPipe::usage =
  "UnsubscribeFromPipe[fun_] removes the \"subscription\"
  to OptionPipe for <fun>";


Begin["`Private`"];


$OptionsInPipe = {};

(* Declare symbol in the `Private` scope *)
subscribed;

OptionPipe /: f_[args___, OptionPipe[opts___?OptionQ]] :=
  Block[{$OptionsInPipe = {opts}}, f[args]];


OptionsInPipe[fun_] := $OptionsInPipe;

unsubscribedQ[fun_] := FreeQ[DownValues[fun], subscribed];

SubscribeToPipe[fun_Symbol?unsubscribedQ] :=
  With[{protected = Unprotect[fun]},
   PrependTo[DownValues[fun],
    HoldPattern[
      fun[args___ /; Not[Or @@ (OptionQ /@ {args})], opts___?OptionQ]
       /; (subscribed;
         True) && (Complement[OptionsInPipe[fun], {opts}] =!= {})] :>
     fun[args, Sequence @@ OptionsInPipe[fun], opts]];
   Protect[protected]];


UnsubscribeFromPipe[fun_] :=
  DownValues[fun] =
   DeleteCases[DownValues[fun], def_ /; Not[FreeQ[def, subscribed]]];


End[];

EndPackage[];



In this way, you don't touch global Options[function] at all, but pass some
options to a function locally during each function call, for those functions
which are "subscribed". Note that I didn't do option filtering above, which
is a bit sloppy on my side, but that can be easily added. Here is how you
can use it:


In[885]:= SubscribeToPipe[fn];

In[886]:= myFunction[]

Out[886]= 9

In[887]:= myFunction[OptionPipe[Display -> True]]

During evaluation of In[887]:= 1   2

Out[887]= 9

You can see that DownValues of the subscribed function have been modified
during "subscription":

In[889]:= DownValues[fn]

Out[889]= {HoldPattern[
   fn[OptionPipe`Private`args$___ /; !
       Or @@ OptionQ /@ {OptionPipe`Private`args$},
     OptionPipe`Private`opts$___?
      OptionQ] /; (OptionPipe`Private`subscribed; True) &&
     Complement[
       OptionsInPipe[fn], {OptionPipe`Private`opts$}] =!= {}] :>
  fn[OptionPipe`Private`args$, Sequence @@ OptionsInPipe[fn],
   OptionPipe`Private`opts$],
 HoldPattern[fn[x_, y_, opts___?OptionQ]] :>
  If[TrueQ[Display /. Flatten[{opts}] /. Options[fn]],
   Print[x, "   ", y]; x + y, x + y]}

When you "unsubscribe", you get back the original defs:

In[891]:= UnsubscribeFromPipe[fn];

In[892]:= DownValues[fn]

Out[892]= {HoldPattern[fn[x_, y_, opts___?OptionQ]] :>
  If[TrueQ[Display /. Flatten[{opts}] /. Options[fn]],
   Print[x, "   ", y]; x + y, x + y]}

The disadvantage of this approach is that the global definitions of the
subscribed functions get modified during all of the "subscription" period.
The advantage is that you pass options locally and have no chance to end up
with options having changed globally. Another advantage is that you can turn
"subscription" on and off dynamically at run-time, so this is more flexible.


Neither one is completely robust, but they worked for me so far. Regarding
their status, IMO both are  hacks. I won't be at all surprised  if someone
suggests some better method than any of these two.

Regards,
Leonid


  • Prev by Date: Re: Re: gaps in plot of piecewise function
  • Next by Date: Re: plot solution derivative
  • Previous by thread: Re: locally changing Options
  • Next by thread: Re: locally changing Options