Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pattern with optional argument in replacement rule

I am trying to define a replacement rule with optional argument color_RGBColor which should be replaced with Sequence[] when it is absent in the original expression:

style[line_Line, ___, 
  color_RGBColor: Unevaluated@Sequence[], ___] :> {color, line}

When RGBColor is present in the original expression, the rule works:

style[Line[], RGBColor[{}]] /. 
 style[line_Line, ___, 
   color_RGBColor: Unevaluated@Sequence[], ___] :> {color, line}

=> {RGBColor[{}], Line[]}

But when it is absent, it does not:

style[Line[], Thickness[0.01]] /. 
 Style[line_Line, ___, 
   color_RGBColor: Unevaluated@Sequence[], ___] :> {color, line}

=> style[Line[], Thickness[0.01]]

My questions are:

1) Why it does not work?

2) Is it possible to construct a single pattern which will work as desired?

like image 985
Alexey Popkov Avatar asked Jun 19 '11 08:06

Alexey Popkov


2 Answers

Your pattern does not work because of the way the pattern-matching works for the default (optional) arguments, and also because you restricted the head to be RGBColor. The problem is that the default argument value must match the pattern, while Unevaluated[Sequence[]] certainly does not match _RGBColor.

You have several ways out. A first attempt is to weaken your type-checking:

In[10]:= style[Line[],Thickness[0.01]]/.
style[line_Line,___,color_: Unevaluated@Sequence[],___]:>{color,line}

Out[10]= {Thickness[0.01],Line[]}

But this does not work since the matching is incorrect - the typing is indeed too weak. The hacky way to make it work is this:

In[14]:= style[Line[], RGBColor[{}]] /. 
 style[line_Line, ___, color : (_RGBColor | _Unevaluated) : 
    Unevaluated@Sequence[], ___] :> {Evaluate@color, line}


Out[14]= {RGBColor[{}], Line[]}

In[15]:= style[Line[], Thickness[0.01]] /. 
  style[line_Line, ___, color : (_RGBColor | _Unevaluated) : 
     Unevaluated@Sequence[], ___] :> {Evaluate@color, line}

Out[15]= {Line[]}

The recommended way to do it is this:

In[18]:= style[Line[], Thickness[0.01]] /. 
style[line_Line, ___, color : (_RGBColor | Automatic) : Automatic, ___] :> 
  If[color === Automatic, {line}, {color, line}]


Out[18]= {Line[]}

In[17]:= style[Line[], RGBColor[{}]] /. 
 style[line_Line, ___, color : (_RGBColor | Automatic) : Automatic, ___] :> 
   If[color === Automatic, {line}, {color, line}]


Out[17]= {RGBColor[{}], Line[]}

This feature of the pattern-matcher is not very widely known, so I will stress it again: the default value for the (optional) pattern x:ptrn:default must match ptrn. For another example of such behavior, see this Mathgroup discussion.

like image 141
Leonid Shifrin Avatar answered Sep 22 '22 23:09

Leonid Shifrin


Perhaps this works for you:

style[Line[a], RGBColor[{}]] /. 
 style[line_Line, ___, Longest[color___RGBColor], ___] :> {color,line}
(*
{RGBColor[{}], Line[a]}
*)

style[Line[]] /. 
 style[line_Line, ___, Longest[color___RGBColor], ___] :> {color, line}
(*
{Line[]}
*)

I guess your replacement rule does not work just because there is no element with a Head RGBColor, so there is no match.

like image 41
Dr. belisarius Avatar answered Sep 19 '22 23:09

Dr. belisarius