在尝试粘贴图片时,我注意到Cases[]
非常慢。
要重现,首先将大图像复制到剪贴板(只需按 Print Screen ),然后评估以下内容:
In[33]:= SetSystemOptions["PackedArrayOptions" -> "UnpackMessage" -> True];
In[34]:= AbsoluteTiming[nb = NotebookGet@ClipboardNotebook[];]
Out[34]= {0.4687500, Null}
In[35]:= AbsoluteTiming[d1 = nb[[1, 1, 1, 1, 1, 1, 1]];]
Out[35]= {0., Null}
In[36]:= AbsoluteTiming[d2 = First@Cases[nb, r_RasterBox :> First[r], Infinity, 1];]
During evaluation of In[36]:= Developer`FromPackedArray::unpack: Unpacking array in call to Notebook. >>
Out[36]= {0.9375000, Null}
(我在Windows上做过这个,不确定其他系统上的粘贴代码是否相同。)
请注意,与直接使用Cases
相比,使用Part
提取数据的速度非常慢,即使我明确告诉Cases
我只需要一次匹配。
我确实发现(如上所示)Cases
由于某种原因触发了解包,即使搜索应该在它到达内部的压缩数组之前停止。使用比Infinity
更浅的级别规范可能会避免解包。
问题:在这里使用Cases
比Part
更容易,也更可靠(如果子表达式出现在不同的位置会怎样?)有没有办法让{ {1}}快到这里,也许是通过使用不同的模式或不同的选项?
可能相关的问题:Mathematica's pattern matching poorly optimized?
(这就是我将Cases
规则从Cases
更改为RasterBox[data_, ___] -> data
的原因。)
答案 0 :(得分:16)
我现在无法访问Mathematica,所以接下来是未经测试的。我的猜测是Cases
在这里解包,因为它首先搜索深度,因此首先看到打包数组。如果这是正确的,那么您可以改为使用规则(ReplaceAll
,而不是Replace
),并在第一次匹配时抛出异常:
Module[{tag},
Catch[
nb /. r_RasterBox :> Block[{}, Throw[First[r], tag] /; True];
$Failed,
tag]
]
正如我所说,这只是一个未经测试的猜测。
在第一次编辑(下面)中,提出了一种相当重的方法。在许多情况下,人们可以选择其他途径。在这个特殊问题(以及许多其他类似问题)中,主要问题是以某种方式屏蔽模式匹配器中的某些子表达式。这也可以通过使用规则来实现,以通过一些虚拟符号临时替换感兴趣的部分。
以下是对Cases
的修改:
Clear[casesShielded];
casesShielded[expr_,pt_,shieldPattern_,levspec_,n_,opts:OptionsPattern[]]:=
Module[{dummy,inverseShieldingRules, shielded, i=0},
inverseShieldingRules =
If[#==={},#,Dispatch@First@#]&@
Reap[shielded= expr/.(p:shieldPattern):>
With[{eval = With[{ind = ++i},Sow[dummy[ind]:>p];dummy[ind]]},
eval/;True];
][[2]];
Cases[shielded,pt,levspec,n,opts]/.inverseShieldingRules];
此版本的Cases
有一个附加参数shieldPattern
(第三个),它指示必须从模式匹配器屏蔽哪些子表达式。
上面的代码非常轻量级(与下面的edit1的建议相比),和它允许完全重用并利用现有的Cases
功能。这适用于主模式(或规则)对相关部件的屏蔽不敏感的情况,这是一种相当常见的情况(特别是涵盖_h
类型的模式,包括手头的情况) 。这也可能比myCases
的应用更快(如下所述)。
在这里,我们需要这个电话:
In[55]:=
(d4=First@casesShielded[nb,x_RasterBox:>First@x,
p_List/;Developer`PackedArrayQ[p],Infinity,1]);//Timing
Out[55]= {0.,Null}
,结果当然和以前一样:
In[61]:= d2===d4
Out[61]= True
我花了一段时间来制作这个功能,我并不是百分之百确定它总能正常工作,但这里有一个Cases
的版本,虽然仍然在深度优先工作,但它总体上分析了表达式在子表达式之前:
ClearAll[myCases];
myCases[expr_, lhs_ :> rhs_, upToLevel_: 1, max : (_Integer | All) : All,
opts : OptionsPattern[]] :=
Module[{tag, result, f, found = 0, aux},
With[{
mopts = FilterRules[{opts}, {Heads -> False}],
frule =
Apply[
RuleDelayed,
Hold[lhs, With[{eval = aux}, Null /; True]] /.
{aux :> Sow[rhs, tag] /; max === All,
aux :> (found++; Sow[rhs, tag])}
]
},
SetAttributes[f, HoldAllComplete];
If[max =!= All,
_f /; found >= max := Throw[Null, tag]
];
f[x_, n_] /; n > upToLevel := Null;
f[x_, n_] :=
Replace[
HoldComplete[x],
{
frule,
ex : _[___] :>
With[{ev =
Replace[
HoldComplete[ex],
y_ :> With[{eval = f[y, n + 1]}, Null /; True],
{2},
Sequence @@ mopts
]},
Null /; True
]
},
{1}
]
]; (* external With *)
result =
If[# === {}, #, First@#] &@
Reap[Catch[f[expr, 0], tag], tag, #2 &][[2]];
(* For proper garbage-collection of f *)
ClearAll[f];
result
]
这不是最简单的代码,所以这里有一些评论。这个版本的Cases
基于我先建议的相同想法 - 即,使用规则替换语义首先尝试对整个表达式进行模式匹配,并且只有在失败时才转到子表达式。我强调这仍然是深度优先遍历,但不同于标准遍历(在Map
,Scan
,Cases
等大多数表达式遍历函数中使用。我使用Reap
和Sow
来收集中间结果(匹配)。这里最棘手的部分是防止子表达式进行评估,我不得不将子表达式包装到HoldComplete
中。因此,我不得不使用(嵌套版本的)Trott-Strzebonski技术(也许,有更简单的方法,但我无法看到它们),以便能够在保持(子)表达式中规则化的rhsides ,并使用具有适当级别规范的Replace
,考虑额外添加的HoldComplete
包装器。我在规则中返回Null
,因为主要操作是Sow
部分,所以最后注入原始表达式的内容并不重要。代码添加了一些额外的复杂性以支持级别规范(我只支持单个整数级别,指示要搜索的最大级别,而不是可能的lev.specs的全部范围),找到的最大结果数量,以及Heads
选项。 frule
的代码用于在我们想要查找所有元素时不会引入计算找到元素的开销。我使用相同的Module
生成的标记作为Sow
的标记,并作为异常的标记(当我找到足够的匹配时,我用它来停止进程,就像我原来的一样建议)。
要对此功能进行非平凡的测试,我们可以查找DownValues
myCases
中的所有符号,并与Cases
进行比较:
In[185]:=
And@@Flatten[
Outer[
myCases[DownValues[myCases],s_Symbol:>Hold[s],#1,Heads->#2] ===
Cases[DownValues[myCases],s_Symbol:>Hold[s],#1,Heads->#2]&,
Range[0,20],
{True,False}
]]
Out[185]= True
myCases
函数比Cases
慢约20-30倍:
In[186]:=
Do[myCases[DownValues[myCases],s_Symbol:>Hold[s],20,Heads->True],{500}];//Timing
Out[186]= {3.188,Null}
In[187]:= Do[Cases[DownValues[myCases],s_Symbol:>Hold[s],20,Heads->True],{500}];//Timing
Out[187]= {0.125,Null}
很容易检查myCases
是否解决了解压缩的原始问题:
In[188]:= AbsoluteTiming[d3=First@myCases[nb,r_RasterBox:>First[r],Infinity,1];]
Out[188]= {0.0009766,Null}
In[189]:= d3===d2
Out[189]= True
希望myCases
对于这种情况通常有用,尽管使用它代替Cases
的性能损失是巨大的,必须加以考虑。