MathGroup Archive 2009

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

Search the Archive

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
> >
> >
> >
>
>


  • Prev by Date: Re: Re: Finding Clusters
  • Next by Date: A Question about Combinatorica
  • Previous by thread: Re: incompatibilities
  • Next by thread: A Question about Combinatorica