Pattern matching repeated structures with named subexpression components

I am trying to write a function with a pattern that tests its arguments for a particular structure. But in that pattern I'd like to name a subpattern to make it easier to exract and operate on.

The code below works fine:

Plant[x:Allele[_,_]..] := Involute[{x}]


But what I really want is to extract the parts of each Allele expression and pass them as lists to Involute. I tried using the definition below (expecting a and b to be Sequences of the arguments to Allele):

Plant[x:Allele[a_,b_]..] := Involute[{x}, {a}, {b}]


Unfortunately, this produces unexpected results when evaluated - specifically it only seems to work on expressions that accept a single Allele argument. When a expand the pattern I get:

 In: (x:Allele[a_,b_]..)//FullForm
Out: Pattern[x, Repeated[Allele[Pattern[a, Blank[]], Pattern[b, Blank[]]]]]


which doesn't seem right. Where's the mistake?

-
Remember to use lowercase for your defined symbols – Dr. belisarius Jan 24 '13 at 2:20
Unless you strongly believe it's a bug, the result isn't nonsensical, but just unexpected to you. It would be helpful to know what you are expecting – Rojo Jan 24 '13 at 2:21
In any case, if it is the pattern expansion what doesn't seem right to you, you'll probably fix it with proper parentheses? – Rojo Jan 24 '13 at 2:26

When you write a pattern like

Plant[x:Allele[a_,b_]..] := Involute[{x}, {a}, {b}]


what you're telling Mathematica is (in words): "When you encounter a call to Plant with one or more arguments, all of which have the head Allele and all of which have the same two arguments, then replace with ...". If that's what you actually meant, then it works as expected. For instance:

Plant[Allele[x, y], Allele[x, y]]
(* Involute[{Allele[x, y], Allele[x, y]}, {x}, {y}] *)


My guess is that this is not what you meant, because this doesn't work when you have plants with different alleles. You probably want to do something like:

Involute[{args of Plant with head Alelle}, {first args to Allele}, {last args to Allele}]


One way to achieve this would be:

Clear@Plant
Plant[x : Allele[_, _] ..] := Involute[{x}, Sequence @@ Transpose[{x} /. Allele -> List]]


which works as follows:

Plant[Allele[a, b], Allele[x, y]]
(* Involute[{Allele[a, b], Allele[x, y]}, {a, x}, {b, y}] *)

-
Indeed, I understand my mistake. I assumed that a_ and b_ just assigned a name to that portion of the expression, but they actually do more. They fix a value to that portion of the expression that then has to match in all subsequent arguments. It seems that MMA pattern matching isn't quite the same as expression destructuring techniques that one finds in languages like Lisp or Clojure. – LBushkin Jan 24 '13 at 3:21
@LBushkin even though rm clearly got your intentions, I insist it would be nice for you to make your question clearer. Your intentions should be understood from the question, for future visitors. Furthermore, I'm intrigued, what were you expecting a_ and b_ to represent? I have no List or Clojure background – Rojo Jan 24 '13 at 3:27
@Rojo - I was expecting a_ and b_ to represent a Sequence of expressions derived by extracting them from the expression x. I will refine my question to make it a bit clearer. – LBushkin Jan 24 '13 at 3:29
@Rojo: This blog article gives some good examples of destructuring in Clojure. – LBushkin Jan 24 '13 at 3:33

Supplementing R.M's answer, here are a few other ways of getting what you may be after:

Plant[x : Allele[_, _] ..] := Involute[{x}, {x}[[All, 1]], {x}[[All, 2]]]

Plant[x : Allele[_, _] ..] := Involute[{x}, ##] & @@ Thread[{x}, Allele]

Plant[x : Allele[_, _] ..] := Thread[{x}, Allele] /. _[ab__] :> Involute[{x}, ab]


Use:

Plant[Allele[i, x], Allele[j, y], Allele[k, z]]

Involute[{Allele[i, x], Allele[j, y], Allele[k, z]}, {i, j, k}, {x, y, z}]


Depending on what these functions do you may need Unevaluated, Hold, or a HoldAll attribute to prevent premature evaluation.

-
Thanks for the answer - I particularly like the [[All,_]] syntax to extract parts from the outer expression. I should have remembered that (I've seen it, but haven't used it much). – LBushkin Jan 24 '13 at 3:24