【发布时间】:2011-07-03 00:04:43
【问题描述】:
在 Mathematica 中,我像这样创建单链表:
toLinkedList[x_List] := Fold[pair[#2, #1] &, pair[], Reverse[x]];
fromLinkedList[ll_pair] := List @@ Flatten[ll];
emptyQ[pair[]] := True;
emptyQ[_pair] := False;
对 cons 单元使用符号 pair 具有 Flatten 安全工作的优点,即使列表包含 Mathematica 样式 Lists,并且允许您使用 MakeExpression/MakeBoxes 定义自定义符号,这使一切变得更加愉快。为了避免使用$IterationLimit,我编写了使用While 循环或NestWhile 来处理这些列表的函数,而不是使用递归。自然,我想看看哪种方法更快,所以我写了两个候选人,以便我可以观看他们的战斗:
nestLength[ll_pair] :=
With[{step = {#[[1, -1]], #[[-1]] + 1} &},
Last@NestWhile[step, {ll, 0}, ! emptyQ@First@# &]];
whileLength[ll_pair] :=
Module[{result = 0, current = ll},
While[! emptyQ@current,
current = current[[2]];
++result];
result];
结果很奇怪。我在长度为 10000 的链表上测试了这些函数,whileLength 通常比nestLength 的 0.055 秒快 50%,大约是 0.035 秒。然而,偶尔whileLength 需要大约 4 秒。我认为可能存在一些缓存行为,所以我开始生成新的随机列表来检查,whileLength 在第一次运行新列表时不一定会很慢;可能需要几十次才能看到减速,但它不会再次发生(至少对于我尝试使用每个列表的 200 次运行来说不是)。
可能会发生什么?
作为参考,我用来测试的函数是这样的:
getTimes[f_, n_] :=
With[{ll = toLinkedList@RandomInteger[100, 10000]},
Table[Timing[f@ll], {n}][[All, 1]]]
编辑:我之前忽略了版本;我使用 Mathematica 8 得到了这些结果。
编辑第二个:当我阅读 Daniel Lichtblau's answer 时,我意识到我的“典型”运行时间省略了前导 0。它已得到修复。
编辑第三个:我认为Leonid Shifrin 将问题与Module 联系起来是正确的;通过将With 替换为Module,我可以从基于NestWhile 的版本中获得相同的行为:
nestModuleLength[ll_pair] :=
Module[{step = {#[[1, -1]], #[[-1]] + 1} &},
Last@NestWhile[step, {ll, 0}, ! emptyQ@First@# &]];
In[15]:= Select[getTimes[nestModuleLength, 100], # > 3 &]
Out[15]= {3.797}
【问题讨论】:
-
Developer'PackedArrayQ 可能相关
-
@Yaroslav Bulatov:我不明白为什么打包数组是相关的,因为除了
RandomInteger生成的初始List之外什么都不应该打包,它会立即转换为树状表达。 -
您使用的是版本 7 还是 8?无论如何,就其价值而言,我认为您已经发现了一些错误,或者至少是评估行为中的一个弱点。
-
我不能把
Module归功于@belisarius 是第一个暗示它是罪魁祸首的人。 -
这不是 With 或 Module 本身(我相信我什至重现了 Block 的问题)。正是符号之间的冲突使评估者误以为它需要检查表达式是否需要重新评估。