I'm trying to build a head that recognizes molecules. Here's the code

Mol::arg = "Invalid molecule.";
Mol[Except[Subscript[_String, _Integer] ..]] := Message[Mol::arg]

I'd expect that this code should recognize molecules of the form

Mol[Subscript["C", 4], Subscript["N", 5]]

but it should issue the message if something like

Mol[Subscript["C", 4], 7]

is entered. However, when I enter this last one, no message is issued. I don't understand what I'm doing wrong.


To simplify the example, consider this:

f[Except[1 ..]] := "match"

{f[1], f[3], f[3, 5]}
{f[1], "match", f[3, 5]}

Except is a single argument, not a sequence. Therefore the definition cannot match when there are multiples arguments of f.

I think Rojo's answer is the cleanest method to get the behavior you want, but another is:

mol::arg = "Invalid molecule.";
pat = Subscript[_String, _Integer]

mol[x__] := With[{}, Message[mol::arg] /; ! MatchQ[{x}, {pat ..}]]

Or, using a trick Rojo showed me to prevent infinite recursion:

Module[{guard = True},
  x : mol[pat ..] /; guard := Block[{guard = False}, x];
      mol[__]     /; guard := Message[mol::arg]
  • $\begingroup$ @Rojo As you say, I just need a wrapper, so that when I pattern match something like _Mol, I can be sure that it is formatted in the correct way. $\endgroup$ – becko May 20 '12 at 17:01
  • $\begingroup$ "Except is a single argument, not a sequence." ... So that explains why my code doesn't work. But then, is there some sort of SequenceExcept in Mathematica? $\endgroup$ – becko May 20 '12 at 17:12
  • $\begingroup$ @becko not that I am aware of. I added a couple more method you could use to my answer. As it stands, my post is the only one that actually answers the question you asked, therefore IMHO you should either Accept it, or (as it seems you prefer) change your answer to move emphasis from why to how. $\endgroup$ – Mr.Wizard May 20 '12 at 20:34
  • 1
    $\begingroup$ @Rojo initial testing indicates Mol[___, Except[Subscript[_String, _Integer]], ___] is still several times faster, probably for this reason. $\endgroup$ – Mr.Wizard May 21 '12 at 9:33
  • 1
    $\begingroup$ @becko you're right, I misapplied Leonid's answer. What I mean is that both of the methods in my answer involve upper-level programming while Rojo's answer is pure pattern matching, even if it does use ___, and perhaps that is why it's faster. I am now well aware that Mathematica's pattern engine has performance issues, and I certainly don't mean to imply that pattern matching will be faster than top level programming. $\endgroup$ – Mr.Wizard May 22 '12 at 12:32

I'd suggest

Mol[___, Except[Subscript[_String, _Integer]], ___]

I think it's an interesting question. I never dug into this but as far as I recall, I never saw Except working for pattern sequences

  • $\begingroup$ This also works. But I think it's slower than Heike's method. Just run ... $\endgroup$ – becko May 20 '12 at 16:46
  • $\begingroup$ molecule=Sequence@@Table[Subscript[RandomChoice[{"C","N","S","H"}],RandomInteger[{1,500}]],{i,1,1000}] $\endgroup$ – becko May 20 '12 at 16:47
  • $\begingroup$ and then this ... $\endgroup$ – becko May 20 '12 at 16:47
  • $\begingroup$ {Timing[Mol1[molecule];], Timing[Mol2[molecule];]} $\endgroup$ – becko May 20 '12 at 16:47
  • 1
    $\begingroup$ @Becko, I get better timings for my version. Averaging 0.000146606 in Heike's and 0.0000437927 in mine, for your example $\endgroup$ – Rojo May 20 '12 at 16:53

You could do something like this

Mol::arg = "Invalid molecule.";
Mol[p__ /; MatchQ[{p}, Except[{Subscript[_String, _Integer] ..}]]] :=  Message[Mol::arg]
  • 1
    $\begingroup$ cool. That works just like I wanted it. But what's wrong my code? And also, isn't the /; a little inefficient compared to other forms of pattern matching? (Not that I care for this particular application about efficiency) $\endgroup$ – becko May 20 '12 at 16:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.