В 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 имеет то преимущество, что Flatten
работает безопасно, даже если списки содержат стиль Mathematica List
s и позволяет вам определять пользовательские нотации с помощью MakeExpression
/MakeBoxes
, что делает все гораздо приятнее. Чтобы избежать необходимости обманывать $IterationLimit
, я написал функции для работы с этими списками, используя либо циклы While
, либо NestWhile
вместо использования рекурсии. Естественно, я хотел посмотреть, какой подход будет быстрее, поэтому я написал двух кандидатов, чтобы я мог наблюдать за их борьбой:
nestLength[ll_pair] :=
With[{step = {#[[1, -1]], #[[-1]] + 1} &},
[email protected][step, {ll, 0}, ! [email protected]@# &]];
whileLength[ll_pair] :=
Module[{result = 0, current = ll},
While[! [email protected],
current = current[[2]];
++result];
result];
Результаты были очень странными. Я тестировал функции на связанных списках длиной 10000, а whileLength
был обычно примерно на 50% быстрее, примерно в 0,035 секунды до nestLength
0,055 секунд. Однако время от времени whileLength
заняло около 4 секунд. Я думал, что может быть какое-то поведение кэширования, поэтому я начал генерировать свежие случайные списки для проверки, а whileLength
не обязательно будет медленным при первом запуске с новым списком; это может занять десятки раз, чтобы увидеть замедление, но тогда оно не будет повторяться (по крайней мере, не для 200 запусков, которые я пытался с каждым списком).
Что может быть?
Для справки, функция, которую я использовал для тестирования, такова:
getTimes[f_, n_] :=
With[{ll = [email protected][100, 10000]},
Table[Timing[[email protected]], {n}][[All, 1]]]
EDIT: Я забыл упомянуть версию раньше; Я получил эти результаты с помощью Mathematica 8.
ИЗМЕНИТЬ второе: Когда я прочитал ответ Даниэля Лихтблау, я понял, что мои времена для "типичных" прогонов опущены ведущим 0. Это было исправлено.
РЕДАКТИРОВАТЬ третий: Я думаю, Леонид Шифрин правилен, чтобы связать проблему с Module
; Я могу получить такое же поведение из версии на основе NestWhile
, заменив With
на Module
:
nestModuleLength[ll_pair] :=
Module[{step = {#[[1, -1]], #[[-1]] + 1} &},
[email protected][step, {ll, 0}, ! [email protected]@# &]];
In[15]:= Select[getTimes[nestModuleLength, 100], # > 3 &]
Out[15]= {3.797}