我将提出一个基于表达式预处理和使用规则的操作的软重新定义的解决方案,而不是规则本身。代码如下:
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 以“软”方式重载使用规则(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]}
预计可以使用格式为fun[expr_,rule_,otherArgs___] 的大多数函数。特别是,这些包括Cases,DeleteCases, Replace, ReplaceAll,ReplaceRepeated。我没有概括为规则列表,但这应该很容易做到。在某些微妙的情况下它可能无法正常工作,例如具有非平凡的头和头上的模式匹配。