Re: How to short-circuit match failure?
- To: mathgroup at smc.vnet.net
- Subject: [mg114255] Re: How to short-circuit match failure?
- From: Leonid Shifrin <lshifr at gmail.com>
- Date: Mon, 29 Nov 2010 06:06:23 -0500 (EST)
Hi kj, Here is one way: In[105]:= ClearAll[foo] foo::toolong = "List is too long"; foo::nolist = "First argument is not a list"; foo::nargs = "foo called with `1` argument(s); 2 expected"; Module[{localfoo}, localfoo[x_List /; Length[x] < 3, y_] := {#, y} & /@ x; localfoo[x_List, y_] := "" /; (Message[foo::toolong]; True) && Throw[$Failed, localfoo]; localfoo[x_, y_] := "" /; (Message[foo::nolist]; True) && Throw[$Failed, localfoo]; localfoo[x___] := "" /; (Message[foo::nargs, Length[{x}]]; True) && Throw[$Failed, localfoo]; foo[args___] := Module[{result}, result = Catch[localfoo[args], localfoo]; result /; result =!= $Failed]]; In[110]:= foo[{1,2,3},3] During evaluation of In[110]:= foo::toolong: List is too long Out[110]= foo[{1,2,3},3] In[111]:= foo[1,3] During evaluation of In[111]:= foo::nolist: First argument is not a list Out[111]= foo[1,3] In[112]:= foo[1,2,3] During evaluation of In[112]:= foo::nargs: foo called with 3 argument(s); 2 expected Out[112]= foo[1,2,3] In[113]:= foo[{1, 2}, 3] Out[113]= {{1, 3}, {2, 3}} Note that by using the local variable <result>, shared between the body and the condition, I am able to ensure the standard semantics of function returning unevaluated, when no applicable rules are found. I don't see an easy way of achieving this without this mechanism. Hope this helps. Regards, Leonid On Sun, Nov 28, 2010 at 2:52 PM, kj <no.email at please.post> wrote: > > > > When defining a function as a sequence of rules (with SetDelayed), > I often want to have messages emitted if the arguments do not have > the proper form, and then *stop* trying any remaining rules, but > I don't know how to do the latter. > > Here's a silly example: > > ClearAll[foo] > foo::toolong = "List 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[x_List, y_] /; Message[foo::toolong] = Null > foo[x_, y_] /; Message[foo::nolist] = Null > foo[x___] /; Message[foo::nargs, Length[{x}]] = Null > > The function foo takes as its first argument a list with length no > greater than 2. But see what happens when foo[{1, 2, 3}, 3] is > evaluated: > > In[86]:= foo[{1, 2, 3}, 3] > During evaluation of In[86]:= foo::toolong: List is too long > During evaluation of In[86]:= foo::nolist: First argument is not a list > During evaluation of In[86]:= foo::nargs: foo called with 2 argument(s); 2 > expected > Out[86]= foo[{1, 2, 3}, 3] > > The result in Out[86] is as desired, but spurious messages were > emitted. It's easy to see why. During the evaluation of foo[{1, > 2, 3}, 3], *all* the rules associated with foo are tried, even > though the third and fourth ones should not be. > > I can prevent this from happening by letting the second rule's > match succeed; e.g. by defining it like this: > > foo[x_List, y_] := (Message[foo::toolong]; Null) > > ...but now evaluating foo[{1, 2, 3}, 3] no longer produces the right > final result (it produces Null, instead of foo[{1, 2, 3}, 3]). > > How can I define the second rule for foo so that the third and > fourth ones are not tried, while the final value for foo[{1, 2, > 3}, 3] remains as foo[{1, 2, 3}, 3]? > > TIA! > > ~kj > > >