Wolfram mathematica 任意深度嵌套模式匹配

Wolfram mathematica 任意深度嵌套模式匹配,wolfram-mathematica,Wolfram Mathematica,有没有一种方法可以创建一个Mathematica模式来匹配头部可能任意深的表达式,即类似于f[\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuz][uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu In[277]:= ArbitrarilyDeepHeadPattern[ head_Symbol] := _?(Function[ MemberQ[ Position[#, _head, {

有没有一种方法可以创建一个Mathematica模式来匹配头部可能任意深的表达式,即类似于
f[\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuz][uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

In[277]:= 
ArbitrarilyDeepHeadPattern[
  head_Symbol] := _?(Function[
    MemberQ[
      Position[#, _head, {0, Infinity}, Heads -> True], {0 ...}]])

In[281]:= Cases[{f[1], g[f[1]], f[1, 2, 3][1], f[1][2][3][4]}, 
 ArbitrarilyDeepHeadPattern[f]]

Out[281]= {f[1], f[1, 2, 3][1], f[1][2][3][4]}
建议的解决办法 似乎没有内置的构造自动对嵌套头进行模式测试。我们可以通过编写一个函数来实现这一目标,该函数对于形式为
f[\uuuuu]…[\uuuuu]
的任何给定(子)表达式,有效地确定
f
(稍微滥用术语,我们可以为表达式调用一个符号头)。代码如下:

ClearAll[shead];
SetAttributes[shead, HoldAllComplete];
shead[expr_] := Scan[Return, Unevaluated[expr], {-1}, Heads -> True];
以下是如何使用它(我将使用与@Sasha相同的测试集):


模式语法 如果您更喜欢使用@Sasha建议的语法,那么该版本如下

Clear[headPattern];
headPattern[head_] := _?(Function[Null, shead[#] === head, HoldFirst]);

In[108]:= Cases[{f[1], g[f[1]], f[1, 2, 3][1], f[1][2][3][4]}, headPattern[f]]

Out[108]= {f[1], f[1, 2, 3][1], f[1][2][3][4]}

进一步解释和评论 工作原理 下面是一些关于这个解决方案的逻辑提示,以及它是如何工作的。如果我们能够利用一些内置的表达式遍历函数,那么该解决方案将是最简洁和高效的。我想到的有
地图
扫描
案例
地图索引
位置
。考虑到我们需要头部,我们需要传递
heads->True
选项。我使用了
Scan
,因为这一个很容易在任何一点停止(不同于其他提到的构造,通常需要抛出一个异常来“在中间”停止它们,这相当不雅观,也会导致一些开销),只要我们找到我们想要的。我们的结果将是
Scan
在其深度优先表达式遍历中找到的第一件事,因此预期它将非常高效(它不会遍历整个表达式)

避免评估漏洞 另一个评论是关于评估。您可以看到
HoldAllComplete
属性在
shead
中使用,而
Unevaluated
在其主体中使用。这些是非常重要的-它们用于防止对传递给函数的表达式进行可能的求值。在这种情况下,这可能很重要:

In[110]:= m = n = 0;
g[x_] := n++;
h[x_] := m++;
{Cases[Hold[f[g[1]][h[2]]], x_ /; shead[x] === f :> Hold[x], Infinity], {m, n}}

Out[113]= {{Hold[f[g[1]][h[2]]]}, {0, 0}}
在这里,我们看到了我们所期望的-即使
Cases
已经遍历了整个表达式并将其(子)部分馈送给
shead
,但是
shead
不会触发子部分的求值。现在我们定义了一个原始版本的
shead
,它“泄漏评估”:

现在

In[114]:= {Cases[Hold[f[g[1]][h[2]]], x_ /; sheadEval[x] === f :> Hold[x], Infinity], {m, n}}

Out[114]= {{Hold[f[g[1]][h[2]]]}, {2, 1}}
后一种行为通常不令人满意。整个代码都是数据范例,在元编程中非常有用,在Mathematica中非常强大,因为您可以使用规则来解构代码。模式匹配过程中可能的(不需要的)评估将极大地损害它。整个问题在子部分。包装
Hold
只会阻止对整个表达式求值。像
Cases
这样的函数和其他类似的代码分解函数非常棒,因为它们在进行结构(语法)匹配时不计算子部分

论符号头
这里的最后一条评论(主要是关于定义)是
shead
函数返回的不是Mathematica中通常所称的符号头。区别在于原子表达式。例如,
shead[f]
返回
f
,而对于原子表达式,真正的符号头应该与表达式的头重合(
Symbol
)。我已经开发了具有此行为的
symbolicHead
函数,在上面的
shead
中也可以成功地使用该函数,尽管
shead
效率更高。

这里可以使用递归匹配策略:

curried[head_] := _head | (x_[___] /; MatchQ[Hold[x], _[curried[head]]])
用法:

In[26]:= $testCases = {f, f[1], g[f[1]], f[1,2,3][1], f[1][2][3][4]};
         Cases[$testCases, curried[f]]

Out[27]= {f[1],f[1,2,3][1],f[1][2][3][4]}
更新

根据Leonid的建议,
Unevaluated
可以作为一种更清晰、更快的方法来避免模式条件下的评估泄漏:

curried[head_] := _head | (x_[___] /; MatchQ[Unevaluated[x], curried[head]])

花环的回答让我重新审视了一个递归定义,我昨天尝试过,但放弃了

我现在意识到我所做的实际上是可行的,它只是抛出了一个错误。与Leonid的精细方法相比,它是一个玩具,但我喜欢简洁的代码,所以我把它贴在这里是出于兴趣或娱乐。运行此操作之前,请确保未将
$RecursionLimit
设置为无穷大

Cases[
  {f, f[1], g[f[1]], f[1, 2, 3][1], f[1][2][3][4]}, 
  f // Blank@#|#0[#]@_&
]
甚至:

Cases[
  {f, f[1], g[f[1]], f[1, 2, 3][1], f[1][2][3][4]},
  p=_f|p@_
]

这里是@Leonid的
shead
的另一个版本,用于查找表达式的符号头。(您应该按原样使用其余部分。)我的函数不涉及任何递归,而是使用具有特殊情况的递归,将levelspec设置为
{-1}
返回所有原子表达式,其中第一个是头本身:

shead2[expr_] := First@Level[expr, {-1}, Heads -> True];
它在模式匹配中的作用与shead的作用相同:

In[264]:= Cases[{f[1], g[f[1]], f[1, 2, 3][1], f[1][2][3][4]}, 
 x_ /; shead2[x] === f]

Out[264]= {f[1], f[1, 2, 3][1], f[1][2][3][4]}
为了帮助理解它是如何工作的,下面是将levelspec设置为
{-1}
Level
的行为:

In[263]:= 
Level[#, {-1}, Heads -> True] & /@ {f[1], g[f[1]], f[1, 2, 3][1], 
  f[1][2][3][4]}

Out[263]= {{f, 1}, {g, f, 1}, {f, 1, 2, 3, 1}, {f, 1, 2, 3, 4}}

在线程中回答了类似的问题。但他的代码只适用于固定深度的表达式。欢迎使用StackOverflow。看看你是否对这个网站有疑问。请记住要有好的答案。+1,对我来说,这个问题在尝试匹配
导数[\][\][\\][\]
时最常出现,每次都很烦人。如果我能多次投票,我会的。您是大师级讲师。@Wizard先生谢谢!但正是我们基于评论的讨论促使我在这里这样做。我希望我总是能找到合适的措辞来回答我的问题。递归模式的一个很好的用法-+1。这就是如何做到这一点的。这是我尝试的第一件事,我立即进行了无限递归。虽然我非常喜欢这个想法,但我必须说,这个解决方案的性能甚至比中等深度嵌套的头部还要差。以下备选方案
curriedAlt[head]:=\uhead |(x_[\uuuu]/;MatchQ[Unevaluated[x],curriedAlt[head]])
提供了更好的性能,但基本上是相同的想法。我们可以看出其中的区别
In[264]:= Cases[{f[1], g[f[1]], f[1, 2, 3][1], f[1][2][3][4]}, 
 x_ /; shead2[x] === f]

Out[264]= {f[1], f[1, 2, 3][1], f[1][2][3][4]}
In[263]:= 
Level[#, {-1}, Heads -> True] & /@ {f[1], g[f[1]], f[1, 2, 3][1], 
  f[1][2][3][4]}

Out[263]= {{f, 1}, {g, f, 1}, {f, 1, 2, 3, 1}, {f, 1, 2, 3, 4}}