在Mathematica中有两种明显的方法来构建链表,“左”:
{1, {2, {3, {4, {5, {6, {7, {}}}}}}}}
和“对”:
{{{{{{{{}, 7}, 6}, 5}, 4}, 3}, 2}, 1}
可以通过以下方式进行:
toLeftLL = Fold[{#2, #} &, {}, Reverse@#] & ;
toRightLL = Fold[List, {}, Reverse@#] & ;
如果我使用这些,并执行一个简单的ReplaceRepeated
来浏览链接列表,我会得到截然不同的Timing
结果:
r = Range[15000];
left = toLeftLL@r;
right = toRightLL@r;
Timing[i = 0; left //. {head_, tail_} :> (i++; tail); i]
Timing[i = 0; right //. {tail_, head_} :> (i++; tail); i]
(* Out[6]= {0.016, 15000} *)
(* Out[7]= {5.437, 15000} *)
为什么?
答案 0 :(得分:8)
ReplaceRepeated
使用SameQ
来确定何时停止应用规则。
当SameQ
比较两个列表时,它会检查长度,如果相同,则将SameQ
应用于从第一个到最后一个的元素。在left
的情况下,第一个元素是整数,因此很容易检测不同的列表,而对于right
列表,第一个元素是深层嵌套的表达式,因此需要遍历它。这就是缓慢的原因。
In[25]:= AbsoluteTiming[
Do[Extract[right, ConstantArray[1, k]] ===
Extract[right, ConstantArray[1, k + 1]], {k, 0, 15000 - 1}]]
Out[25]= {11.7091708, Null}
现在将其与:
进行比较In[31]:= Timing[i = 0; right //. {tail_, head_} :> (i++; tail); i]
Out[31]= {5.351, 15000}
<小时/> 编辑回应Mr.Wizard关于加快提升速度的选项问题。人们应该写一个自定义相同的测试。
ReplaceRepeated
未提供此类选项,因此我们应使用FixedPoint
和ReplaceAll
:
In[61]:= Timing[i = 0;
FixedPoint[(# /. {tail_, _} :> (i++; tail)) &, right,
SameTest ->
Function[
If[ListQ[#1] && ListQ[#2] &&
Length[#1] ==
Length[#2], (#1 === {} && #2 === {}) || (Last[#1] ===
Last[#2]), #1 === #2]]]; i]
Out[61]= {0.343, 15000}
<小时/> EDIT2 :更快:
In[162]:= Timing[i = 0;
NestWhile[Function[# /. {tail_, head_} :> (i++; tail)], right,
Function[# =!= {}]]; i]
Out[162]= {0.124, 15000}