15

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.

Sjoerd C. de Vries
  • 65,815
  • 14
  • 188
  • 323
a06e
  • 11,327
  • 4
  • 48
  • 108

3 Answers3

13

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

Brett Champion
  • 20,779
  • 2
  • 64
  • 121
Rojo
  • 42,601
  • 7
  • 96
  • 188
10

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]
]
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • @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. – a06e May 20 '12 at 17:01
  • "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? – a06e May 20 '12 at 17:12
  • @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. – Mr.Wizard May 20 '12 at 20:34
  • I thought of this, but at the time you were about the only one other than me that found it cool, so I got depressed, you know. Now you made me happy using it. My bet it that it's the fastest of all, but can't test right now, only in about half an hour. Let me know if you test first ;D – Rojo May 20 '12 at 22:51
  • I accepted @Rojo answer before because, as you say, it's the neatest solution to the how question. But you're right that your answer is the only one that truly fits my question. – a06e May 20 '12 at 23:27
  • @Rojo I can't test the speed right now either, but will do it soon and post here. – a06e May 20 '12 at 23:47
  • 1
    @Rojo initial testing indicates Mol[___, Except[Subscript[_String, _Integer]], ___] is still several times faster, probably for this reason. – Mr.Wizard May 21 '12 at 09:33
  • @Mr.Wizard In the answer you reference, Leonid says "(...) an instance of the general behavior of the pattern-matcher when used with what I call syntactic patterns - patterns which only reflect the rigid structure of an expression, like e.g. _f." I don't think ___, Except[Subscript[_String, _Integer]], ___ qualifies as a syntactic pattern. – a06e May 22 '12 at 03:11
  • In a very uninformed and merely intuitive guess, I'd say that Heike's p__ /; MatchQ[{p}, Except[{Subscript[_String, _Integer] ..}]] is easier to match than @Rojo ___, Except[Subscript[_String, _Integer]], ___. – a06e May 22 '12 at 03:13
  • @Mr.Wizard I followed some of the links in the answer you referenced. Leonid says that MatchQ slows pattern matching, but so does double blanks __ ... – a06e May 22 '12 at 03:27
  • 1
    @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. – Mr.Wizard May 22 '12 at 12:32
9

You could do something like this

Clear[Mol];
Mol::arg = "Invalid molecule.";
Mol[p__ /; MatchQ[{p}, Except[{Subscript[_String, _Integer] ..}]]] :=  Message[Mol::arg]
Heike
  • 35,858
  • 3
  • 108
  • 157
  • 1
    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) – a06e May 20 '12 at 16:28