MathGroup Archive 2010

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

Search the Archive

Re: How to short-circuit match failure?

  • To: mathgroup at smc.vnet.net
  • Subject: [mg114370] Re: How to short-circuit match failure?
  • From: Leonid Shifrin <lshifr at gmail.com>
  • Date: Thu, 2 Dec 2010 05:40:39 -0500 (EST)

Hi kj,

A nice solution! It offers a good way of separating error-checking code from the
actual one (so that in principle, error - checking code could be generated
programmatically - one just has to parse the declarations (patterns) of the
actual
code).

One small remark: you don't actually have to use the argsok variable,
due to the way conditional definitions work (core[args] is only evaluated if
 chkargs[args], which is evaluated first, gives true). So, this will work
just as well:

ClearAll[foo]
foo::toolong = "First argument is too long";
foo::nolist = "First argument is not a list";
foo::nargs = "foo called with `1` argument(s); 2 expected";
foo[args___] :=
 Module[{chkargs, core},
  chkargs[x_List /; Length[x] < 3, y_] := True;
  chkargs[_List, _] := Message[foo::toolong];
  chkargs[_, _] := Message[foo::nolist];
  chkargs[x___] := Message[foo::nargs, Length[{x}]];
  core[x_, y_] := {#, y} & /@ x;
  core[args] /; chkargs[args]]


Regards,
Leonid



On Wed, Dec 1, 2010 at 10:14 AM, kj <no.email at please.post> wrote:

>
> Thanks for all your comments and suggestions.  Inspired by them,
> I came up with this general approach for defining a function that
> emits diagnostic messages when called with incorrect arguments,
> and returns the original expression (unevaluated).
>
> The idea is to replace something like this:
>
> ClearAll[foo]
> foo::toolong = "First argument is too long";
> foo::nolist = "First argument is not a list";
> foo::nargs = "foo called with `1` argument(s); 2 expected";
> foo[x_List /; Length[x] < 3, y_] := {#, y} & /@ x
> foo[_List, _] /; Message[foo::toolong] = Null
> foo[_, _] /; Message[foo::nolist] = Null
> foo[x___] /; Message[foo::nargs, Length[{x}]] = Null
>
> with something like this:
>
> ClearAll[foo]
> foo::toolong = "First argument is too long";
> foo::nolist = "First argument is not a list";
> foo::nargs = "foo called with `1` argument(s); 2 expected";
> foo[args___] := Module[{chkargs, argsok, core, othervars},
>
>  chkargs[x_List /; Length[x] < 3, y_] := True;
>
>  chkargs[_List, _] := Message[foo::toolong];
>  chkargs[_, _] := Message[foo::nolist];
>  chkargs[x___] := Message[foo::nargs, Length[{x}]];
>
>  core[x_, y_] := {#, y} & /@ x;
>
>  argsok = chkargs[args];
>  argsok && core[args] /; argsok
>  ]
>
> The function is defined as a *single* (delayed) rule that evaluates
> to a module with a standard structure.  The key points are these:
>
> 1. the rule has a single catchall pattern (args___), and therefore
>   it will apply to all invocations of the function;
>
> 2. all the argument-checking is transferred basically verbatim to
>   a module-local rule called checkargs; this rule evaluates to
>   True whenever the arguments are valid, or to a call to Message
>   with the appropriate diagnostic; this terminates the argument-checking,
>   so any remaining chkargs rules are skipped;
>
> 3. the function's main action (which assumes all the arguments are
>   ok) is defined as one or more module-local delayed rules called
>   core; since all the argument matching and checking is delegated
>   to the chkargs rule(s), the pattern(s) for the core rule(s) can
>   be more general (i.e. simpler) than would be required otherwise
>   (e.g. in the example above, the pattern is simply x_, y_);
>
> 4. the last two lines of the module are standard (always the same);
>   in the next-to-last line, argument checking is done by evaluating
>   chkargs[args} and assigning the reuslt to the module-local
>   variable argsok; in the process, diagnostic messages are emitted
>   if the checking fails; in this case, argsok will have value
>   Null; otherwise, it will have value True;
>
> 5. in the last line, if argsok is True, core[args] is evaluated,
>   and returned as the module's evaluation value; if argsok is not
>   True (i.e.  it is Null), core[args] is not evaluated, and
>   furthermore the main module's evaluation will fail thanks to
>   the failed /; argsok condition at the end, so the final evaluation
>   will yield the original expression, unevaluated;
>
> 6. one feature not illustrated by the simple example above is that
>   the DownValues of the chkargs rules can be more elaborate, and
>   even set the process of variables local to the main module
>   (represented by othervars in the example); these variables can
>   be shared with other chkargs rules, or core rules, and thus
>   avoid having to perform some calculations needed by several of
>   them more than once.
>
> This approach seems pretty general to me, and it is basically a
> wrapper around the "standard" approach.
>
> As always, your comments and criticisms are always welcome.
>
> ~kj
>
>


  • Prev by Date: Re: Why are my 3D plots blue?
  • Next by Date: Re: multiple outputs from compiled function
  • Previous by thread: Re: How to short-circuit match failure?
  • Next by thread: Re: How to short-circuit match failure?