Я не уверен, почему вы хотите "создать и выбросить" ленивую последовательность, созданную функцией range
. Ограниченная итерация, выполняемая dotimes
, вероятно, более эффективна, так как представляет собой встроенное приращение и сравнивается с каждым шагом, но вы можете заплатить дополнительные расходы, чтобы выразить свою собственную конкатенацию списка.
Типичное решение Lisp состоит в добавлении новых элементов в список, который вы строите по ходу работы, а затем в обратном порядке разрушает этот составной список, чтобы получить возвращаемое значение. Другие методы, позволяющие добавлять в список в постоянное время, хорошо известны, но они не всегда оказываются более эффективными, чем подход prepend-then-reverse .
В Clojure вы можете использовать переходные процессы , чтобы добраться туда, полагаясь на деструктивное поведение функции conj!
:
(let [r (transient [])]
(dotimes [i 10]
(conj! r (* i i))) ;; destructive
(persistent! r))
Кажется, это работает, но документация по переходным процессам предупреждает, что не следует использовать conj!
для "разбивки значений на месте" - то есть, чтобы рассчитывать на разрушительное поведение вместо того, чтобы ловить возвращаемое значение Следовательно, эту форму необходимо переписать.
Чтобы привязать r
выше к новому значению, которое выдает каждый вызов conj!
, нам нужно будет использовать atom , чтобы ввести еще один уровень косвенности. Однако в этот момент мы просто боремся против dotimes
, и было бы лучше написать собственную форму, используя loop
и recur
.
Было бы неплохо иметь возможность предварительно выделить вектор того же размера, что и границы итерации. Я не вижу способа сделать это.