10
$\begingroup$

update: Thanks for the answers below. But I noticed they do not handle the following special cases: lis=1 and lis=z , lis=1/z and lis=-z as these are also considered legal input as given in the question below. It is easy to add special case processing for these, by making them lis={1} and lis={z} etc... before calling the parser (this is what I actually had to do myself). but it will be nice if this was incorporated as special pattern/rule as well. I could not do it myself inside Alternative and had to check for this before as special case.

To make sure what is the input, I'll clarify it again: Each term is of the form c*z^n where there can be one or more terms. i.e. lis= term1+term2+term3...

In each term, c can be any numerical value, and n can be any numerical value (positive or negative). The result will be list of {{c,n},.....}, one entry for each term. Even if there is one term only, it should work.

Examples to verify against:

lis=1   --->  {{1,0}}
lis=z ----> {{1,1}}
lis=-z --->{{-1,1}}
lis=1/z  ---->{{1,-1}}
lis=1+z^-1 --->{{1,0},{1,-1}}
lis=1/z^3 + 3/z + 3 z + z^3 -->{{1,-3},{3,-1},{3,1},{1,3}}
lis=6 + 1/z^4 + 4/z^2 + 4 z^2 + z^4 -->{{6, 0}, {1, -4}, {4, -2}, {4, 2}, {1, 4}}

Original question

The hammer I use now for everything I see that needs parsing with patterns is Cases.

So, I was trying to write a simple pattern based parser that accepts any output from Expand(f[z]) where f[z] is rational function of z only. For example, it take such input as

Expand[(z + 1/z)^4]
(*6 + 1/z^4 + 4/z^2 + 4 z^2 + z^4*)

or

Expand[(z + 1/z)^3]
(*1/z^3 + 3/z + 3 z + z^3*)

or 1 or z or z^-1 or 1+z and so on. So the input can be thought of as power series in z. All terms are in this form. So it is well defined what it is.

The output of the parser will be a list that shows the coefficient of each term, and the exponent of z. So given the input 6+(1/z^4) + 4/z^2 + 4 z^2 + z^4 the output will be

 {{6,0},{1,-4},{4,-2},{4,2},{1,4}}

The best I can come up with is this:

lis = 6 + 1/z^4 + 4/z^2 + 4 z^2 + z^4
Cases[lis, Alternatives[x_. Power[z, e_.], 
                       Times[x_, Power[z, e_.]], 
                       x_.] :> {x, e}]

The problem is that the above generates

  {{6},{1,-4},{4,-2},{4,2},{1,4}}

and not

  {{6,0},{1,-4},{4,-2},{4,2},{1,4}}

This is because the pattern that matched x_. (the last one in the Alternatives does not have an e in it, and Mathematica replaced the e with the Null sequence as documented in help. Another problem with the above, is that x_. has to the last one in the alternatives. If I had wrote this instead:

Cases[lis, Alternatives[x_., x_. Power[z, e_.], Times[x_, Power[z, e_.]]] :> 
       {x, e}]

Then it would not have worked (another thing to worry about).

Ok, this is all fine, except now the output is not symmetric and needs special case processing.

What I was hoping to do is something like this

 Cases[lis, Alternatives[x_. Power[z, e_.] :> {x,e} , 
                         Times[x_, Power[z, e_.]] :> {x,e}, 
                         x_. :> {x,0}] 
       ]

ie. have a specific rule for each specific pattern. The above is much easier to work with, since now I can specific the output for each pattern instead of having all pattern target one output. But the above of course is not valid Mathematica code and Cases does not like it.

Are there such general ways of doing the pattern matching that allows one to write specific rules for specific pattern? It does not have to use Cases as long as it follows this general approach.

I know that one can probably use the Polynomial specific functions (such as CoefficientRules, CoefficientList, and related functions to do this for this case, but I am more interested in this general approach so I can learn how to use it for things other that this example)

$\endgroup$

4 Answers 4

8
$\begingroup$

Just define a new function that could take different inputs, either through overloading or using If, Which, or Switch.

Clear[f]
f[x_. Power[z, e_.]] := {x, e}
f[Times[x_, Power[z, e_.]]] := {x, e}
f[x_] := {x, 0}

Cases[lis, y : Alternatives[x_., x_. Power[z, e_.], Times[x_, Power[z, e_.]]] :> f[y]]
(* {{6, 0}, {1, -4}, {4, -2}, {4, 2}, {1, 4}} *)

Update: Below is the most general way I could think of to achieve what the OP wants while still using Cases.

HoldPattern is needed to prevent z^0 from evaluating before it is matched. FreeQ is needed to make sure that the matched coefficient is free from z**:

Clear[coeff, z]

(* Applies to plus expressions *)
coeff[expr_Plus] := 
 Cases[expr, HoldPattern[(c : _?(FreeQ[#, z] &) : 1) (zVar : z^e_. : z^0)] :> {c, e}]

(* Applies to standalone expressions: nest them in a list first before using Cases *)
coeff[expr_] := 
 Cases[{expr}, HoldPattern[(c : _?(FreeQ[#, z] &) : 1) (zVar : z^e_. : z^0)] :> {c, e}]

{#, coeff@#} & /@ {1, z, -z, 1/z, 1 + z^-1, 1/z^3 + 3/z + 3 z + z^3, 6 + 1/z^4 + 4/z^2 + 4 z^2 + z^4} // 
 Grid[Prepend[#, {"expr", "coeff[expr]"}], Frame -> All] &

Mathematica graphics

**

Otherwise things like this might happen:

Cases[{z^2}, HoldPattern[(c : _ : 1) (zVar : z^e_. : z^0)] :> {c, e}]
(* {{z^2, 0}} *)

That's because z^2 matches the first term, while z^0 matches the second term. This is not what we want, which is 1 for the first term, and z^2 for the second term.

$\endgroup$
5
$\begingroup$

One possibility is to use Replace with Sow/Reap:

Scan[
   Replace[#, {
      x_. Power[z, e_.] :> Sow[{x, e}],
      Times[x_, Power[z, e_.]] :> Sow[{x, e}],
      x_ :> Sow[{x, 0}]}] &,
   lis] // Reap // #[[-1, -1]] &

{{6, 0}, {1, -4}, {4, -2}, {4, 2}, {1, 4}}

However, the wildcard x_ could be tricky if you needed to operate deeper in the expression.

It seems like there should be a way to use Cases and insert Sow inside Condition (/;) but I have not managed to get that approach working. Maybe someone else can figure it out :)

$\endgroup$
5
$\begingroup$

The General Case

Alas, Cases does not permit multiple replacement rules. But Replace does, although we must take care to 1) create an output list, 2) omit non-matching elements and 3) operate only upon the first level:

lis = 6 + 1/z^4 + 4/z^2 + 4 z^2 + z^4;

Replace[List @@ lis, {x_. z^e_. :> {x, e}, x_ :> {x, 0}, _ :> Sequence[]}, {1}]

(* {{6,0},{1,-4},{4,-2},{4,2},{1,4}} *)

This Specific Case

Cases can do the job at hand if we explicitly note the possibility that e might be bound to an empty sequence:

Cases[lis, x_. z^e_. | x_ :> ({x, e} /. {s_} :> {s, 0})]

(* {{6,0},{1,-4},{4,-2},{4,2},{1,4}} *)

What Could Have Been...

By rights, we should have been able to bind a value to e in alternatives from which it is missing by writing:

Cases[lis, x_. z^e_. | PatternSequence[x_, e_:0] :> {x, e}]

... but unfortunately this expression does not do what we want due to a long-standing bug in Mathematica:

MatchQ[{1, 2}, {a_, b_}]                     (* True *)
MatchQ[{1, 2}, {a_, b_:0}]                   (* True *)
MatchQ[{1, 2}, {PatternSequence[a_, b_]}]    (* True *)
MatchQ[{1, 2}, {PatternSequence[a_, b_:0]}]  (* False -- BUG! *)
$\endgroup$
2
5
$\begingroup$

Another possibility is to use ReplaceAll on the rhs of RuleDelayed in the second argument of Cases:

rules = Alternatives[x_. Power[z, e_.] :> {x, e}, Times[x_, Power[z, e_.]] :> {x, e},
                     x_. :> {x, 0}]; (* your prefered second argument for Cases *)

Cases[lis, pat : rules[[All, 1]] :> (pat /. List @@ rules)]
(*  {{6, 0}, {1, -4}, {4, -2}, {4, 2}, {1, 4}} *)

Update:

exp = {1, z, -z, 1/z, 1 + z^-1, 1/z^3 + 3/z + 3 z + z^3, 6 + 1/z^4 + 4/z^2 + 4 z^2 + z^4};
cF = Cases[If[Head[#] === Plus, #, {#}], 
           pat : rules[[All, 1]] :> (pat /. List @@ rules)] &    (* thanks: @algohi *)

Grid[{{"expr", "cF[expr]"}, ## & @@ ({#, cF@#} & /@ exp)}, Frame -> All]

enter image description here

$\endgroup$
2
  • $\begingroup$ You can edit you answer to be like this : Cases[If[Head[lis] === Plus, lis, {lis}], pat : rules[[All, 1]] :> (pat /. List @@ rules)] to take all cases of the OP. $\endgroup$ Commented Aug 26, 2014 at 6:27
  • $\begingroup$ @Algohi, thank you so much. That was one of the things I tried after Nasser's update, but somehow I could not get it to work. Now -- with a fresh session -- it works :) $\endgroup$
    – kglr
    Commented Aug 26, 2014 at 6:42

Your Answer

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

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