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

发布于 2024-11-17 04:39:11 字数 413 浏览 2 评论 0原文

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

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

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

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

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

您知道有什么简单、简洁、通用的方法可以做到这一点吗?

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

I would like to be able to have a pattern that matches only expressions that are (alternately: are not) children of certain other elements.

For example, a pattern to match all Lists not within a Graphics object:

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

This pattern would match {1,2,3} but not {{1,2},{3,4}}.

There are relatively easy ways to extract expressions matching these criteria, but patterns are not only for extraction, but also for replacement, which is my main use case here (ReplaceAll).

Do you know of any easy, concise, and general ways to do this?

Is it possible to do this at all with just patterns?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

独夜无伴 2024-11-24 04:39:11

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

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 中的泄漏函数抽象。这里的另一个技术细节是使用 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。我没有概括为规则列表,但这应该很容易做到。在某些微妙的情况下,它可能无法正常工作,例如,对于重要的头部和头部的模式匹配。

I will propose a solution based on expression pre-processing and soft redefinitions of operations using rules, rather than rules themselves. Here is the code:

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;
]

A few details on implementation ideas, and how it works. The idea is that, in order to restrict the pattern that should match, we may wrap this pattern in some head (say h), and also wrap all elements matching the original pattern but also being (or not being) within some other element (matching the "parent" pattern) in the same head h. This can be done for generic "child" pattern. Technically, one thing that makes it possible is the intrusive nature of rule application (and function parameter-passing, which have the same semantics in this respect). This allows one to take the rule like x_List:>f[x], matched by generic pattern lhs_:>rhs_, and change it to h[x_List]:>f[x], generically by using h[lhs]:>rhs. This is non-trivial because RuleDelayed is a scoping construct, and only the intrusiveness of another RuleDelayed (or, function parameter-passing) allows us to do the necessary scope surgery. In a way, this is an example of constructive use of the same effect that leads to the leaky functional abstraction in Mathematica. Another technical detail here is the use of UpValues to overload functions that use rules (Cases, ReplaceAll, etc) in the "soft" way, without adding any rules to them. At the same time, UpValues here allow the code to be universal - one code serves many functions that use patterns and rules. Finally, I am using the Module variables as a mechanism for encapsulation, to hide the auxiliary head h and function preprocess. This is a generally very handy way to achieve encapsulation of both functions and data on the scale smaller than a package but larger than a single function.

Here are some examples:

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]}

It is expected to work with most functions which have the format fun[expr_,rule_,otherArgs___]. In particular, those include Cases,DeleteCases, Replace, ReplaceAll,ReplaceRepeated. I did not generalize to lists of rules, but this should be easy to do. It may not work properly in some subtle cases, e.g. with non-trivial heads and pattern-matching on heads.

梦途 2024-11-24 04:39:11

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

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

我认为可以使用 ReplaceAll 一次性完成。我们可以在这里依赖 ReplaceAll记录的功能>:它不会查看原始表达式中已被替换的部分,即使它们被自身替换!引用文档:“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 in这个例子):

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}}]]]}

According to your explanation in the comment to the acl's answer:

Actually I'd like it to work at any
level in the expression <...>. <...>
what I need is replacement: replace
all expression that match this
"pattern", and leave the rest
unchanged. I guess the simplest
possible solution is finding the
positions of elements, then using
ReplacePart. But this can also get
quite complicated in the end.

I think it could be done in one pass with ReplaceAll. We can rely here on the documented feature of the ReplaceAll: it does not look at the parts of the original expression which were already replaced even if they are replaced by themselves! Citing the Documentation: "ReplaceAll looks at each part of expr, tries all the rules on it, and then goes on to the next part of expr. The first rule that applies to a particular part is used; no further rules are tried on that part, or on any of its subparts."

Here is my solution (whatIwant is what you want to do with matched parts):

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

Here is your test case:

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

Here is a function that replaces only inside certain head (Graphics in this example):

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

Here is a test case:

replaceChildren[{{1, 2, 3}, Graphics[Line[{{1, 2}, {3, 4}}]]}] // InputForm
=> {{1, 2, 3}, Graphics[Line[whatIwant[{{1, 2}, {3, 4}}]]]}
静水深流 2024-11-24 04:39:11

您可以编写一个递归函数,该函数沿表达式树向下延伸,并且仅当位于正确类型的子表达式内时才对所需的表达式类型起作用,而忽略其他所有内容。模式将在函数的定义中大量使用。

例如,考虑以下表达式。

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]

You might write a recursive function that descends an expression tree and acts on the types of expression you want only if inside the right type of sub-expression, while leaving everything else alone. Patterns would be used heavily in the definition of the function.

Consider, for example, the following expression.

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

Let's suppose that we want to rotate every ordered pair that we see in the first argument of Graphics about the origin through the angle Pi/4, while leaving other points alone. The following function does this.

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;

Now we check

f[test]
杯别 2024-11-24 04:39:11

我可能误解了你的意思,但是,如果我理解正确的话,你想要匹配所有带有头 List 的表达式,这些表达式具有这样的属性:在表达式树中向上,我们永远不会遇到 图形。我不知道如何一次性完成此操作,但如果您愿意匹配两次,您可以执行类似

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[___]] & 完成的,它返回 {randhead[5], {1, 2, {3, 5}} }),然后选择返回列表中带有 Head List 的内容。请注意,我在 lst 中添加了更多内容。

但想必您知道这一点并且正在寻找单一模式来完成这项工作?

I am probably misunderstanding you, but, if I do understand correctly you want to match all expressions with head List which have the property that, going upwards in the expression tree, we'll never meet a Graphics. I m not sure how to do this in one pass, but if you are willing to match twice, you can do something like

lst = {randhead[5], {1, 2, {3, 5}}, Graphics[Line[{{1, 2}, {3, 4}}]]};
Cases[#, _List] &@Cases[#, Except@Graphics[___]] &@lst
(*
----> {{1, 2, {3, 5}}}
*)

which first selects elements so that the Head isn't Graphics (this is done by Cases[#, Except@Graphics[___]] &, which returns {randhead[5], {1, 2, {3, 5}}}), then selects those with Head List from the returned list. Note that I've added some more stuff to lst.

But presumably you knew this and were after a single pattern to do the job?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文