7
$\begingroup$

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?

$\endgroup$
3
  • $\begingroup$ Remember to use lowercase for your defined symbols $\endgroup$ Jan 24, 2013 at 2:20
  • 3
    $\begingroup$ 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 $\endgroup$
    – Rojo
    Jan 24, 2013 at 2:21
  • $\begingroup$ In any case, if it is the pattern expansion what doesn't seem right to you, you'll probably fix it with proper parentheses? $\endgroup$
    – Rojo
    Jan 24, 2013 at 2:26

2 Answers 2

9
$\begingroup$

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}] *)
$\endgroup$
4
  • $\begingroup$ 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. $\endgroup$
    – LBushkin
    Jan 24, 2013 at 3:21
  • $\begingroup$ @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 $\endgroup$
    – Rojo
    Jan 24, 2013 at 3:27
  • $\begingroup$ @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. $\endgroup$
    – LBushkin
    Jan 24, 2013 at 3:29
  • $\begingroup$ @Rojo: This blog article gives some good examples of destructuring in Clojure. $\endgroup$
    – LBushkin
    Jan 24, 2013 at 3:33
8
$\begingroup$

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.

$\endgroup$
1
  • $\begingroup$ 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). $\endgroup$
    – LBushkin
    Jan 24, 2013 at 3:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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