Re: Re: Passing function arguments as lists of
- To: mathgroup at smc.vnet.net
- Subject: [mg104741] Re: [mg104683] Re: [mg104661] Passing function arguments as lists of
- From: Leonid Shifrin <lshifr at gmail.com>
- Date: Sun, 8 Nov 2009 06:48:23 -0500 (EST)
- References: <3794357.1257503749087.JavaMail.root@n11>
Hi Leo, The advantage of using Options mechanism is the increased safety during the pattern-matching, since built-ins such as OptionValue - OptionsPattern, or older OptionQ provide a sort of a type-check. The newer constructs OptionValue - OptionsPattern provide more safety than OptionQ, since OptionValue inspects a list of global Options to make sure that the passed option is known to the function. The older OptionQ seems however easier to understand since it is based only on the standard pattern-matching and isn't directly related to any of the global properties. Whether or not you want this extra safety provided by any of these constructs is up to you, but my guess is that most people find it useful, especially for larger projects. One reason why these type checks are really useful is that often options are passed as parameters by functions in a chain-like manner, filtered, etc., so without such checks some of the pattern-matching errors would be very hard to catch since they would be causing harm "far away" from the place of their origin. If you want to maintain a logical separation between options and named parameters, here a a few possibilities. First is to use a kind of SubValue-based definition, like this: ClearAll[f]; parameters[f] = {"npar1" -> 1, "npar2" -> 2, "npar3" -> 3}; Options[f] = {"option1" -> 4, "option2" -> 5}; f[a_, b_, pars___?OptionQ][opts___?OptionQ] := Module[{npar1, npar2, npar3, option1, option2}, {npar1, npar2, npar3} = {"npar1", "npar2", "npar3"} /. Flatten[{pars}] /. parameters[f]; {option1, option2} = {"option1", "option2"} /. Flatten[{opts}] /. Options[f]; Print["Positional paramaters: ", a, " ", b, "\n Named parameters ", npar1, " ", npar2, " ", npar3, "\n Options ", option1, " ", option2]] The usage will be something like In[1] := f[a,b,"npar2"-> 10]["option1"->20] Positional paramaters: a b Named parameters 1 10 3 Options 20 5 Here I introduced a new (user-defined) container <parameters>, to store defaults for the named parameters. This uses the old OptionQ option-testing predicate. You could also use OptionsPattern: ClearAll[ff]; Options[ff] = {"option1" -> 4, "option2" -> 5, "npar1" -> 1, "npar2" -> 2, "npar3" -> 3}; ff[a_, b_, npars : OptionsPattern[]][opts : OptionsPattern[]] := Module[{npar1, npar2, npar3, option1, option2}, {npar1, npar2, npar3} = OptionValue[ff, {npars}, #] & /@ {"npar1", "npar2", "npar3"}; {option1, option2} = OptionValue[ff, {opts}, #] & /@ {"option1", "option2"}; Print["Positional paramaters: ", a, " ", b, "\n Named parameters ", npar1, " ", npar2, " ", npar3, "\n Options ", option1, " ", option2]] But here, to take advantage of OptionValue, we have to store all defaults in Options[ff]. Of course you can also mix things, keeping OptionsPattern but not using OptionValue for your named parameters, and instead using something like <parameters> and the old option-passing mechanism. You can also use a different signature - for example make your named arguments be the optional very first argument and options - the last one: g[npars : OptionsPattern[], a_, b_, opts : OptionsPattern[]] :=... This lets you avoid SubValues if for some reason you don't want to use them. In this case however, you must make sure that your first normal parameter (a_ here) does not match the option pattern, to avoid ambiguities. Note that if you use named parameters next to options in your arguments list, you may end up with ambigous pattern-matching and some of your parameters may be interpreted as options or vice versa. Of course, there are plenty of other possible solutions. For example, wrap named parameters in some container to which you give a special meaning, like <namedParameters> below g[a_, b_,namedParameters[npars : OptionsPattern[]], opts : OptionsPattern[]] :=... and keep the argument namedParameters[] present but empty if you don't want to pass any named parameters explicitly. The latter inconvenience can be removed by overloading a function like this: g[a_, b_, opts : OptionsPattern[]] :=.g[a,b,namedParameters[],opts], which lets you use the shorter form if no named parameters need to be passed. Yet another possibility is to make a special option called say "namedParameters", and pass it whenever you need to pass some parameters, like g[a,b,"namedParameters"->{"npar1"->value1,...}] This has the advantage of both using the standard mechanism and maintaining the logical separation between options and parameters, but the "type-check" for the list of parameters is no longer there (you can of course implement it with some extra code). The final comment is that whatever style you choose, it will start to pay off when there will be many functions which you will treat consistently according to this style, and it should be as "mindless" to use as possible. Using the standard mechanisms has many advantages, such as a better integration with both the exisitng and not yet existing functionality, to name just one. Hope this helps. Regards, Leonid On Sat, Nov 7, 2009 at 3:45 AM, Leo Alekseyev <dnquark at gmail.com> wrote: > This is certainly one option, but does it have any benefits over the > list of string replacement rules?.. > > I recently started using both the options pattern _and_ a replacement > list. The actual functions I'm working with look something like > > foo[x_,y_,parameters_,OptionsPattern[]], > > where x and y are bona fide dependent variables, parameters is a list > of fixed constants pertinent to the system (provided as replacement > rules of the form, e.g. {"a"->1,...}, and OptionsPattern[] conveys > certain options pertaining to the computation itself, e.g. > {AvoidDivideByZero->True} > > So far it seems like a sensible approach, but I am still wondering if > other people do similar things in their code. > > --Leo > > On Fri, Nov 6, 2009 at 8:57 AM, David Park <djmpark at comcast.net> wrote: > > Use the newer Version 6 Options facilities. > > > > ClearAll[foo] > > Options[foo] = {a -> 0, b -> 0, c -> 0}; > > SyntaxInformation[foo] = {"ArgumentsPattern" -> {OptionsPattern[]}}; > > foo[OptionsPattern[]] := > > Module[ > > {aval = OptionValue[a], > > bval = OptionValue[b], > > cval = OptionValue[c]}, > > {aval, bval, cval}] > > > > foo[a -> 1, b -> 2] > > {1, 2, 0} > > > > Then try typing: > > > > foo[a -> 1, q -> 2] > > > > > > David Park > > djmpark at comcast.net > > http://home.comcast.net/~djmpark/ <http://home.comcast.net/%7Edjmpark/> > > > > > > From: dnquark [mailto:dnquark at gmail.com] > > > > > > I wish to (a) avoid having to pass a dozen parameters to a function > > and have to deal with remembering which goes where and (b) retain the > > flexibility in terms of adding new parameters. It seems that a good > > solution would be to pass my parameters as a structure with named > > fields (this is how I do it in another system). In Mathematica, I came= > up > > with something like this: > > > > foo[paramList_] := Module[{a,b,c}, > > {a,b,c} = {"a","b","c"}/.paramList; > > {a,b,c} > > ] > > sample usage: e.g. foo[{"a"->1,"b"->2}] > > > > My question to the group: is this a good solution? Are there better > > ways to achieve my goals?.. > > Thanks, > > --Leo > > > > > > > >