Wolfram mathematica 模式测试未优化?
在准备回应时,我遇到了一个意想不到的问题,这是我自己的行为 请考虑:Wolfram mathematica 模式测试未优化?,wolfram-mathematica,pattern-matching,Wolfram Mathematica,Pattern Matching,在准备回应时,我遇到了一个意想不到的问题,这是我自己的行为 请考虑: test = (Print[##]; False) &; MatchQ[{1, 2, 3, 4, 5}, {x__?test, y__}] 我承认以前从未探索过模式匹配的这一方面,我对这一方面似乎效率低下感到不安。这真的和看起来的一样糟糕吗,还是发生了某种自动缓存打印显示为针对该项进行指示 有没有一种更有效的基于模式的方法来编写这篇文章 正确的模式匹配行为是否需要这种冗余级别,为什么 我匆忙地做出了一个错误的断
test = (Print[##]; False) &;
MatchQ[{1, 2, 3, 4, 5}, {x__?test, y__}]
我承认以前从未探索过模式匹配的这一方面,我对这一方面似乎效率低下感到不安。这真的和看起来的一样糟糕吗,还是发生了某种自动缓存<代码>打印显示为针对该项进行指示
- 有没有一种更有效的基于模式的方法来编写这篇文章
- 正确的模式匹配行为是否需要这种冗余级别,为什么
我匆忙地做出了一个错误的断言,但我离开了它,因为下面的好答案解决了这个问题。请在以后的回答中忽略它 很容易证明在其他领域也进行了类似的优化 案例: (没有打印任何内容。) 假的 在这里,Mathematica甚至从不测试任何元素的
y
这里只是一个快速猜测,但是
{{uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。因此,{-1,2,3,4,5}
在第一个元素上失败,因此没有打印输出。尝试用((Print[###];Positive[#])&)替换正片
,您将看到它的行为方式与第一个模式相同 我认为你关于Mathematica优化模式测试的假设是错误的。考虑模式<代码> {xy},yy}} /COD>。正如您正确地说的那样,{1,2,3,4,5}
有4种方式可以适应这种模式。如果现在使用?test
,将模式测试添加到此中,MatchQ
应返回True
,前提是4种方式中的任何一种与模式匹配。因此,它必须测试所有可能的选项,直到找到匹配项为止。因此,只有当有积极因素时才进行优化,而不是当模式失败时
下面是对您的示例的一个修改,它使用了正数
,这表明Mathematica并没有像您所说的那样进行优化:
test2 = (Print@##; Positive@##) &;
MatchQ[{-1, 2, 3, 4, 5}, {x__?test2, y__}]
它打印了4次!如果第一个元素确实为正(因此不必测试其余元素),它将以一个打印正确终止。第二个元素的测试仅在第一个元素为True
时应用,这就是为什么在您的元素中未应用y
测试的原因。这里有一个不同的例子:
test3 = (Print@##; Negative@##) &;
MatchQ[{1, 2, 3, 4, -5}, {x__?test2, y__?test3}]
我同意你的逻辑,如果第一个失败了,那么考虑到每个元素都应该匹配,所有后续的可能性也将失败。我的猜测是Mathematica还没有那么聪明,对
、
或\uuuuuuuu
中的任何一个实现相同的行为要比为每个实现不同的行为更简单。根据您使用的是\uuuuu
还是\uuuuu
实现不同的行为将掩盖测试的真实性质,并使调试变得更困难。好的,我来试试
MatchQ[{1, 2, 3, 4, 5}, {x__?test, y__}]//Trace
显示插槽序列是冗余的,因为只测试了一个元素,在本例中是序列中的第一个元素。e、 g
MatchQ[{1, 2, 3, 4, 5}, {_, x__?test, y__}] // Trace
现在打印3个2。如果将第一个模式更改为BlankSequence,将生成更多排列,其中一些排列具有不同的第一个元素
MatchQ[{1, 2, 3, 4, 5}, {__, x__?test, y__}] // Trace
在最后一个例子中
MatchQ[{-1, 2, 3, 4, 5}, {__?Positive, y__?test}]
第一个元素总是无法通过测试。如果你把这个改成
MatchQ[{1, 2, -3, 4, 5}, {__?Positive, y__?test}]
您将看到更多与您预期相符的内容。我认为每个人都忘记了测试函数中可能存在的副作用。以下是我认为正在发生的事情:正如Wizard先生和其他人提到的,有几种方式可以匹配模式,只是组合。对于{x}
和{y}
的每个组合,首先测试x
模式。顺便说一下,定义多个参数的函数(##
)没有意义,因为正如@Simon所解释的,测试函数分别应用于序列中的每个元素。这也解释了为什么只打印第一个元素(-1):一旦找到第一个不匹配的元素,模式匹配器就会停止并继续测试下一个可用的组合
下面是一个更具说明性的示例:
In[20]:=
MatchQ[{-1,2,3,4,5},{_,x__?(Function[Print[#];Positive[#]])}]
During evaluation of In[20]:= 2
During evaluation of In[20]:= 3
During evaluation of In[20]:= 4
During evaluation of In[20]:= 5
Out[20]= True
现在它会打印所有这些文件,因为函数会一个接一个地应用于这些文件,就像在本例中一样
现在是问题的关键。在这里,我设置了一个带有副作用的测试函数,它决定在测试第一个元素后改变主意:
Module[{flag = False},
ClearAll[test3];
test3[x_] :=
With[{fl = flag},
If[! flag, flag = True];
Print[x];
fl
]
];
第一个组合(即{-1},{2,3,4,5}
将被拒绝,因为函数首先给出False
。但是,第二个组合({-1,2},{3,4,5}
)将被接受。这正是我们观察到的:
In[22]:=
MatchQ[{-1,2,3,4,5},{x__?test3,y__}]
During evaluation of In[22]:= -1
During evaluation of In[22]:= -1
During evaluation of In[22]:= 2
Out[22]= True
图案匹配器一找到匹配项,打印就停止了
现在,从这里可以明显看出,问题和其他一些答案中提到的优化通常是不可能的,因为模式匹配器无法控制测试函数中可能存在的可变状态
在考虑模式匹配时,我们通常将其视为一个独立于评估的过程,这在很大程度上是正确的,因为模式匹配器是系统的一个内置组件,一旦模式和表达式进行评估,它将接管并在很大程度上绕过主评估循环模式匹配功能更强大,但代价是与求值器纠缠在一起。这包括使用条件
和模式测试
,因为这两个是“入口点”将主评估过程转换为与之隔离的模式匹配过程。一旦模式匹配器命中其中一个,它就会在要测试的条件下调用主评估器,然后一切都是可能的。这让我再次看到,当使用模式进行测试时,模式匹配器是最有效的测试
和条件
都不存在,而且模式完全是语法模式——在这种情况下,它可以优化<
MatchQ[{-1, 2, 3, 4, 5}, {__?Positive, y__?test}]
MatchQ[{1, 2, -3, 4, 5}, {__?Positive, y__?test}]
In[20]:=
MatchQ[{-1,2,3,4,5},{_,x__?(Function[Print[#];Positive[#]])}]
During evaluation of In[20]:= 2
During evaluation of In[20]:= 3
During evaluation of In[20]:= 4
During evaluation of In[20]:= 5
Out[20]= True
Module[{flag = False},
ClearAll[test3];
test3[x_] :=
With[{fl = flag},
If[! flag, flag = True];
Print[x];
fl
]
];
In[22]:=
MatchQ[{-1,2,3,4,5},{x__?test3,y__}]
During evaluation of In[22]:= -1
During evaluation of In[22]:= -1
During evaluation of In[22]:= 2
Out[22]= True