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
- References:
- locally changing Options
- From: hemmecke <hemmecke@gmail.com>
- locally changing Options