【问题标题】:Pattern to match only "children" of certain elements仅匹配某些元素的“子项”的模式
【发布时间】:2011-06-23 09:16:16
【问题描述】:

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

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

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

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

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

您知道任何简单、简洁和通用的方法吗?

是否有可能只用模式来做到这一点?

【问题讨论】:

  • 与 {{{{1,2,3}}},Cycles[{{1,2,3},{4,5,6} 这样的输入对应的所需输出是什么}], 图形[Line[{{1,2},{3,4}}]] }?

标签: wolfram-mathematica pattern-matching


【解决方案1】:

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

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。我没有概括为规则列表,但这应该很容易做到。在某些微妙的情况下它可能无法正常工作,例如具有非平凡的头和头上的模式匹配。

【讨论】:

  • @Szabolcs 嗯,这并不复杂,至少想法很简单。我已经编辑了帖子并添加了一段解释一些想法和实施细节的段落。
【解决方案2】:

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

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

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

这是我的解决方案(whatIwant 是您想要对匹配部件执行的操作):

replaceNonChildren[lst_List] := 
 ReplaceAll[#, {x_List :> whatIwant[x], y_ :> y}] & /@ lst

这是你的测试用例:

replaceNonChildren[{{1, 2, 3}, Graphics[Line[{{1, 2}, {3, 4}}]]}] // InputForm
=> {whatIwant[{1, 2, 3}], Graphics[Line[{{1, 2}, {3, 4}}]]}

这是一个仅替换特定头部内部的函数(本例中为Graphics):

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

这是一个测试用例:

replaceChildren[{{1, 2, 3}, Graphics[Line[{{1, 2}, {3, 4}}]]}] // InputForm
=> {{1, 2, 3}, Graphics[Line[whatIwant[{{1, 2}, {3, 4}}]]]}

【讨论】:

  • 啊,这是一个替换被某个其他表达式包围的表达式的好技巧(我们可以在上面的示例中使用y_Graphics)。但是仍然存在在某个头部内替换事物的情况。 :)
  • 在替换中替换,也不错。
【解决方案3】:

您可能会编写一个递归函数,该函数下降表达式树并仅在正确类型的子表达式内对您想要的表达式类型进行操作,而将其他所有内容都放在一边。在函数的定义中会大量使用模式。

例如,考虑以下表达式。

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

假设我们想要将Graphics 的第一个参数中看到的每个有序对围绕原点旋转 Pi/4 角,同时不理会其他点。以下函数执行此操作。

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]

【讨论】:

    【解决方案4】:

    我可能误解了你,但是,如果我理解正确,你想匹配所有带有头部 List 的表达式,它们具有在表达式树中向上的属性,我们永远不会遇到 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[___]] &amp; 完成的,它返回{randhead[5], {1, 2, {3, 5}}}),然后从返回的列表中选择具有Head List 的元素。请注意,我在lst 中添加了更多内容。

    但大概你知道这一点,并且只需要一种模式来完成这项工作吗?

    【讨论】:

    • 实际上我希望它可以在表达式的任何级别上工作,但是在您的示例中很容易通过使用 DeleteCases 在任何级别摆脱 Graphics 来解决这个问题。但更大的问题是我需要的是替换:替换所有匹配这个“模式”的表达式,其余的保持不变。我想最简单的解决方案是找到元素的位置,然后使用ReplacePart。但这最终也会变得相当复杂。
    • @Szabolcs 是的,您在问题中确实提到了这一点。好吧,我也没有看到任何直接的方法。也许其他人会。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-11
    • 1970-01-01
    相关资源
    最近更新 更多