# Deep Pattern matching with repeating arguments

I have a function that accepts a single argument that is described by the following grammar:

term := (Less | Greater | Equal | LessEqual | GreaterEqual)[Symbol,Number]
argument := term | Or[term,term,...]


The expressions are generated from operations like:

Reduce[x^2 -x -6x == 0, x, Reals] // FullForm
(* Or[Equal[x,-2],Equal[x,3]] *)


I want to use a pattern in the function definition to ensure that the argument is of the correct form and I want to convert the argument into the following form for use within the function:

List[term,...]


I am currently bogged down in pattern purgatory. I have tried all of the following and am confused by the results.

(* Test data *)
s = Or[Equal[x,-2],GreaterEqual[x,3]]


This pattern matched the test case. So far so good.

(* case 1 *)
s /. Or[(Less | Greater | Equal | LessEqual | GreaterEqual)
[x_Symbol, (_Integer | _Rational | _Real)] ..] :> {2}
(* {2} V {2} *)


Let's see if we can capture the head in a variable. Yes, we can. I've got this figured out.

(* case 2 *)
s /. f_Symbol[(Less | Greater | Equal | LessEqual | GreaterEqual)
[x_Symbol, (_Integer | _Rational | _Real)] ..] :> {f}
(* {Or} *)


Let's limit the pattern to a specific head instead of any symbol. This didn't match. I'm not clear on why.

(* case 3 *)
s /. f_Or[(Less | Greater | Equal | LessEqual | GreaterEqual)
[x_Symbol, (_Integer | _Rational | _Real)] ..] :> {f}
(* No Match *)


Maybe a different syntax will work. And it does, but I expected {Or} as a result.

(* case 4 *)
s /. f : (Or)[(Less | Greater | Equal | LessEqual | GreaterEqual)
[x_Symbol, (_Integer | _Rational | _Real)] ..] :> {f}
(* {x == -2} V {x >= 3} *)


Edit: Some appropriately placed parens make a difference. I understand this result, but I still don't understand the previous one.

(* case 5)
s /. (f : Or)[(Less | Greater | Equal | LessEqual | GreaterEqual)
[x_Symbol, (_Integer | _Rational | _Real)] ..] :> {f}
(* {Or} *)


End Edit

I am missing something important about how patterns/rules work. I would like to correct that gap in knowledge. And, secondarily, I'd like to get my function to only accept an appropriate argument and be able to convert the argument into the desired format.

-
f_Or won't match Or[f] because it is already evaluated and becomes f –  rm -rf Feb 26 '13 at 21:48
f_Or matches something with head Or, not exactly Or. So, _Or[stuff] matches Or[something][stuff] –  Rojo Feb 26 '13 at 22:08
@Rojo Does that same logic apply to f_Symbol? Because the pattern with f_Symbol[...] matches while the one with f_Or[...] does not. I assumed that f_Or was simply a more specific version of f_Symbol. Is that not the case? –  RandomBits Feb 26 '13 at 22:24
@rm-rf Why would f_Or ever match Or[f]? I am not following that part of your comment. I do understand why you need to use HoldPattern for Or[f]. –  RandomBits Feb 26 '13 at 22:28
It is not the case. Symbol is a symbol, and Or is another symbol. One isn't a subset of the other. All symbols have the "invisible head" Symbol. With that in mind, Or matches _Symbol. f_Or would be "equivalent" to f:Or[___] which would be a subset of f:_Symbol[___] –  Rojo Feb 26 '13 at 22:28
show 4 more comments

## 2 Answers

Why not just this?

f[expr_] := Flatten[{expr /. Or -> List}]

f /@ {Reduce[x == 4, x, Reals], Reduce[x^2 == 4, x, Reals],
Reduce[x^2 > 4, x, Reals], Reduce[x^2 >= 4, x, Reals],
Reduce[x^2 < 4, x, Reals]}

-
add comment

In the comments to my question above, @Rojo et. al. helped me figure out what I was missing with using patterns.

Based on that help I was able to figure out the answer to the second part of my question. Here is the code that I am using:

Remove[ftermQ];
Remove[f];
ftermQ[expr_] := Module[
{
termQ = MatchQ[#, (Less | Greater | Equal | LessEqual | GreaterEqual)] &,
numberQ = MatchQ[#, (_Integer | _Rational | _Real)] &
},
MatchQ[expr, _?termQ[_Symbol, _?numberQ]] ||
MatchQ[expr, Inequality[_, Less, _Symbol, Less, _]] ||
MatchQ[expr, Inequality[_, LessEqual, _Symbol, LessEqual, _]]
]

f[term_?ftermQ | HoldPattern[Or[term : (_?ftermQ) ..]]] := {term}

f /@ {Reduce[x == 4, x, Reals],
Reduce[x^2 == 4, x, Reals],
Reduce[x^2 > 4, x, Reals],
Reduce[x^2 >= 4, x, Reals],
Reduce[x^2 < 4, x, Reals]}
(**
{{x==4},
{x==-2,x==2},
{x<-2,x>2},
{x<=-2,x>=2},
{-2<x<2}}
**)


At this point the only obvious issue is the pattern::patv messages produced because I use term_ as both a single and multiple element.

-
add comment