模式仅匹配某些元素的"子元素"

Sza*_*lcs 8 wolfram-mathematica pattern-matching

我希望能够拥有一个模式,该模式只匹配某些其他元素的子项(可选地:不是).

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

{ {1,2,3}, Graphics[Line[{{1,2},{3,4}}]] }
Run Code Online (Sandbox Code Playgroud)

这种模式会匹配{1,2,3}但不匹配{{1,2},{3,4}}.

有相对简单的方法来提取符合这些条件的表达式,但模式不仅用于提取,还用于替换,这是我的主要用例here(ReplaceAll).

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

只用模式就可以做到这一点吗?

Leo*_*rin 8

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

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;
]
Run Code Online (Sandbox Code Playgroud)

关于实施思路的一些细节,以及它的工作原理.我们的想法是,为了限制应该匹配的模式,我们可以将这个模式包装在某个头部(例如h)中,并且还包装与原始模式匹配的所有元素,但也包含(或不存在)在其他元素中(匹配) "父母"模式)在同一个头脑中h.这可以用于通用的"子"模式.从技术上讲,使一切成为可能的是规则应用程序的侵入性(以及函数参数传递,它在这方面具有相同的语义).这允许人们采用像x_List:>f[x]通用模式匹配的规则lhs_:>rhs_,并h[x_List]:>f[x]通过使用将其更改为h[lhs]:>rhs.这是非平凡的,因为它RuleDelayed是一个范围构造,只有另一个RuleDelayed(或函数参数传递)的侵入性允许我们进行必要的范围手术.在某种程度上,这是相同效果的建设性使用的一个例子,导致Mathematica中的漏洞功能抽象.这里的另一个技术细节是使用UpValues过载使用规则(函数Cases,ReplaceAll在"软"的方式,等等),不添加任何规则给他们.同时,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]}
Run Code Online (Sandbox Code Playgroud)

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

  • @Szabolcs嗯,这并不是很复杂,至少这些想法很简单.我编辑了这篇文章,并添加了一段解释一些想法和实施细节的段落. (2认同)

Ale*_*kov 7

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

实际上我希望它能在表达式<...>中的任何级别工作.<...>我需要的是替换:替换所有匹配此"模式"的表达式,并保持其余部分不变.我想最简单的解决方案是找到元素的位置,然后使用 ReplacePart.但最终这也会变得相当复杂.

我认为可以一次性完成ReplaceAll.我们可以依赖于以下文档的特征ReplaceAll:它不会查看原始表达式中已被替换的部分,即使它们被自己替换!引用文档:" ReplaceAll查看每个部分expr,尝试其中的所有规则,然后继续下一部分expr.使用适用于特定部分的第一条规则;不再对该部分进行规则,或者在任何子部分上."

这是我的解决方案(whatIwant是你想要匹配的部分):

replaceNonChildren[lst_List] := 
 ReplaceAll[#, {x_List :> whatIwant[x], y_ :> y}] & /@ lst
Run Code Online (Sandbox Code Playgroud)

这是你的测试用例:

replaceNonChildren[{{1, 2, 3}, Graphics[Line[{{1, 2}, {3, 4}}]]}] // InputForm
Run Code Online (Sandbox Code Playgroud)
=> {whatIwant[{1, 2, 3}], Graphics[Line[{{1, 2}, {3, 4}}]]}

这是一个只替换某个头部内部的函数(Graphics在本例中):

replaceChildren[lst_List] := 
 ReplaceAll[#, {y : Graphics[__] :> (y /. x_List :> whatIwant[x])}] & /@ lst
Run Code Online (Sandbox Code Playgroud)

这是一个测试用例:

replaceChildren[{{1, 2, 3}, Graphics[Line[{{1, 2}, {3, 4}}]]}] // InputForm
Run Code Online (Sandbox Code Playgroud)
=> {{1, 2, 3}, Graphics[Line[whatIwant[{{1, 2}, {3, 4}}]]]}