模式仅匹配某些元素的“子元素”

时间:2011-06-23 09:16:16

标签: wolfram-mathematica pattern-matching

我希望能够拥有一个只匹配某些其他元素的子代(或者不是)的表达式的模式。

例如,匹配List对象内所有Graphics的模式:

{ {1,2,3}, Graphics[Line[{{1,2},{3,4}}]] }

此模式符合{1,2,3}但不匹配{{1,2},{3,4}}

提取符合这些条件的表达式的方法相对简单,但模式不仅适用于提取,还适用于 replacement ,这是我的主要用例( ReplaceAll)。

你知道任何简单,简洁和一般的方法吗?

是否可以只使用模式进行此操作?

4 个答案:

答案 0 :(得分:7)

我将提出一个基于表达式预处理和使用规则的操作软重新定义的解决方案,而不是规则本身。这是代码:

ClearAll[matchChildren, exceptChildren];
Module[{h, preprocess},
  preprocess[expr_, parentPtrn_, lhs_, match : (True | False)] :=
     Module[{pos, ptrnPos, lhsPos},
       ptrnPos = Position[expr, parentPtrn];
       lhsPos = Position[expr, lhs];
       pos = Cases[lhsPos, {Alternatives @@ PatternSequence @@@ ptrnPos, __}];
       If[! match,pos = Complement[Position[expr, _, Infinity, Heads -> False], pos]];
       MapAt[h, expr, pos]];

  matchChildren /: 
    fun_[expr_, matchChildren[parentPtrn_, lhs : Except[_Rule | _RuleDelayed]],
    args___] :=
       fun[preprocess[expr, parentPtrn, lhs, True], h[lhs], args] //. 
           h[x_] :> x;

  matchChildren /: 
    fun_[expr_, matchChildren[parentPtrn_, lhs_ :> rhs_], args___] :=
       fun[preprocess[expr, parentPtrn, lhs, True], h[lhs] :> rhs, args] //. 
           h[x_] :> x;

  exceptChildren /: 
   fun_[expr_,exceptChildren[parentPtrn_, lhs : Except[_Rule | _RuleDelayed]], 
   args___] :=
       fun[preprocess[expr, parentPtrn, lhs, False], h[lhs], args] //. 
           h[x_] :> x;

  exceptChildren /: 
   fun_[expr_, exceptChildren[parentPtrn_, lhs_ :> rhs_], args___] :=
       fun[preprocess[expr, parentPtrn, lhs, False], h[lhs] :> rhs, args] //. 
          h[x_] :> x;
]

有关实施思路及其运作方式的一些细节。我们的想法是,为了限制应该匹配的模式,我们可以将这个模式包含在某个头部(比如h)中,并且还包含与原始模式匹配的所有元素,但也包括(或不存在)在同一个头h中的一些其他元素(匹配“父”模式)。这可以用于通用的“子”模式。从技术上讲,使一切成为可能的是规则应用程序的侵入性(以及函数参数传递,它在这方面具有相同的语义)。这允许我们采用x_List:>f[x]这样的规则,与通用模式lhs_:>rhs_匹配,并通过使用h[x_List]:>f[x]将其更改为h[lhs]:>rhs。这是非平凡的,因为RuleDelayed是一个范围构造,只有另一个RuleDelayed(或函数参数传递)的侵入性允许我们进行必要的范围外科手术。在某种程度上,这是建设性地使用导致Mathematica中leaky functional abstraction的相同效果的一个例子。这里的另一个技术细节是使用UpValues以“软”方式重载使用规则(CasesReplaceAll等)的函数,而不向它们添加任何规则。同时,UpValues允许代码是通用的 - 一个代码服务于许多使用模式和规则的函数。最后,我使用Module变量作为封装机制,隐藏辅助头h和函数preprocess。这是一种非常方便的方法,可以在小于包但比单个函数大的范围内实现功能和数据的封装。

以下是一些例子:

In[171]:= expr = {{1,2,3},Graphics[Line[{{1,2},{3,4}}]]};

In[168]:= expr/.matchChildren[_Graphics,x_List:>f[x]]//FullForm
Out[168]//FullForm= List[List[1,2,3],Graphics[Line[f[List[List[1,2],List[3,4]]]]]]

In[172]:= expr/.matchChildren[_Graphics,x:{__Integer}:>f[x]]//FullForm
Out[172]//FullForm= List[List[1,2,3],Graphics[Line[List[f[List[1,2]],f[List[3,4]]]]]]

In[173]:= expr/.exceptChildren[_Graphics,x_List:>f[x]]//FullForm
Out[173]//FullForm= List[f[List[1,2,3]],Graphics[Line[List[List[1,2],List[3,4]]]]]

In[174]:= expr = (Tan[p]*Cot[p+q])*(Sin[Pi n]+Cos[Pi m])*(Tan[q]+Cot[q]);

In[175]:= expr/.matchChildren[_Plus,x_Tan:>f[x]]
Out[175]= Cot[p+q] (Cot[q]+f[Tan[q]]) (Cos[m \[Pi]]+Sin[n \[Pi]]) Tan[p]

In[176]:= expr/.exceptChildren[_Plus,x_Tan:>f[x]]
Out[176]= Cot[p+q] f[Tan[p]] (Cos[m \[Pi]]+Sin[n \[Pi]]) (Cot[q]+Tan[q])

In[177]:= Cases[expr,matchChildren[_Plus,x_Tan:>f[x]],Infinity]
Out[177]= {f[Tan[q]]}

In[178]:= Cases[expr,exceptChildren[_Plus,x_Tan:>f[x]],Infinity]
Out[178]= {f[Tan[p]]}

In[179]:= Cases[expr,matchChildren[_Plus,x_Tan],Infinity]
Out[179]= {Tan[q]}

In[180]:= Cases[expr,matchChildren[_Plus,x_Tan],Infinity]
Out[180]= {Tan[q]}

预计可以使用大多数格式为fun[expr_,rule_,otherArgs___]的函数。特别是那些包括Cases,DeleteCases, Replace, ReplaceAll,ReplaceRepeated。我没有概括到规则列表,但这应该很容易做到。在某些微妙的情况下,它可能无法正常工作,例如具有非平凡的头部和头部的图案匹配。

答案 1 :(得分:6)

根据您对acl答案的评论中的解释:

  

实际上我希望它可以在任何地方工作   表达式< ...>中的级别。 < ...>   我需要的是更换:替换   所有与此匹配的表达式   “模式”,剩下的就剩下了   不变。我猜最简单   可能的解决方案是找到   元素的位置,然后使用   ReplacePart。但这也可以得到   最后很复杂。

我认为可以使用ReplaceAll一次完成。我们可以依赖ReplaceAll的{​​{3}}功能:它不会查看已经替换的原始表达式的部分,即使它们被自己替换!引用文档:“ReplaceAll查看 expr 的每个部分,尝试其中的所有规则,然后继续 {{1的下一部分使用适用于特定部分的第一个规则;不对该部分或其任何子部分尝试进一步的规则。“

这是我的解决方案(expr是您要对匹配的部分做的事情):

whatIwant

以下是您的测试用例:

replaceNonChildren[lst_List] := 
 ReplaceAll[#, {x_List :> whatIwant[x], y_ :> y}] & /@ lst
=> {whatIwant[{1, 2, 3}], Graphics[Line[{{1, 2}, {3, 4}}]]}

这是一个只替换某个头部内部的函数(在本例中为replaceNonChildren[{{1, 2, 3}, Graphics[Line[{{1, 2}, {3, 4}}]]}] // InputForm ):

Graphics

这是一个测试用例:

replaceChildren[lst_List] := 
 ReplaceAll[#, {y : Graphics[__] :> (y /. x_List :> whatIwant[x])}] & /@ lst
=> {{1, 2, 3}, Graphics[Line[whatIwant[{{1, 2}, {3, 4}}]]]}

答案 2 :(得分:3)

您可以编写一个递归函数,该函数使表达式树下降,并且只有在正确类型的子表达式内部时才会对所需的表达式类型起作用,同时保留其他所有内容。模式将在函数的定义中大量使用。

例如,考虑以下表达式。

test = {{1, 2}, Graphics[{
  Point[{{-1, 0}, {1, 0}}],
  Line[{{-1, 0}, {1, 0}}]},
 Frame -> True, 
 PlotRange -> {{-1, 1}, {-0.5, 0.5}}]};

假设我们想要通过角度Pi / 4旋转我们在Graphics的第一个参数中看到的关于原点的每个有序对,同时留下其他点。以下功能可以做到这一点。

Clear[f];
f[{x_?NumericQ, y_?NumericQ}] := If[flag === True,
  RotationMatrix[Pi/4].{x, y}, {x, y}];
f[Graphics[primitives_, rest___]] := Block[{flag = True},
  Graphics[f[primitives], rest]];
f[x_?AtomQ] := x;
f[x_] := f /@ x;

现在我们检查

f[test]

答案 3 :(得分:0)

我可能误解了你,但是,如果我理解正确,你想要匹配所有表达式List的表达式,这些表达式具有在表达式树中向上的属性,我们永远不会遇到{{1} 1}}。我不确定如何在一次通过中做到这一点,但如果你愿意匹配两次,你可以做类似的事情

Graphics

首先选择元素,使lst = {randhead[5], {1, 2, {3, 5}}, Graphics[Line[{{1, 2}, {3, 4}}]]}; Cases[#, _List] &@Cases[#, Except@Graphics[___]] &@lst (* ----> {{1, 2, {3, 5}}} *) 不是Head(由Graphics完成,返回Cases[#, Except@Graphics[___]] &),然后选择{{1}来自返回列表的{}} {}请注意,我已向{randhead[5], {1, 2, {3, 5}}}添加了更多内容。

但据推测,你知道这一点并且只是按照一种模式来完成这项工作吗?