在Mathematica中,我这样创建单向链表:
toLinkedList[x_List] := Fold[pair[#2, #1] &, pair[], Reverse[x]];
fromLinkedList[ll_pair] := List @@ Flatten[ll];
emptyQ[pair[]] := True;
emptyQ[_pair] := False;
使用符号pair
作为cons单元的优点在于,即使列表中包含Mathematica风格的List
s,也可以安全地使用Flatten
函数,并且允许您使用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
快约50%,大约需要0.035秒,而nestLength
则需要0.055秒。但是,偶尔whileLength
会花费约4秒钟的时间。我认为可能存在一些缓存行为,所以开始生成新的随机列表进行检查,使用新列表时whileLength
不一定会变慢;需要多次运行才能看到减速,但然后它就不会再发生(至少对于我每个列表尝试的200次测试而言)。可能出了什么问题?
供参考,我用于测试的函数是这个:
getTimes[f_, n_] :=
With[{ll = toLinkedList@RandomInteger[100, 10000]},
Table[Timing[f@ll], {n}][[All, 1]]]
编辑: 我之前忘了提版本号; 这些结果是使用Mathematica 8得到的。
第二次编辑: 当我阅读Daniel Lichtblau的回答时,我意识到我的“典型”运行时间漏掉了一个前导0。已经修复了。
第三次编辑: 我认为Leonid Shifrin正确地将问题与Module
相关联;通过用Module
替换With
,我可以从基于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}
RandomInteger
生成的初始List
之外,没有任何东西应该被压缩,而这个List
立即被转换成类似树形的表达式。 - PillsyModule
负责 - @belisarius是第一个建议它是罪魁祸首的人。 - Leonid Shifrin