No estoy seguro de por qué le preocupa "crear y tirar" la secuencia diferida creada por la función range
. La iteración acotada realizada por dotimes
es probablemente más eficiente, ya que se trata de un incremento en línea y se compara con cada paso, pero puede pagar un costo adicional para expresar allí su propia concatenación de listas.
La solución típica de Lisp es anteponer nuevos elementos a una lista que construye sobre la marcha, luego invierta esa lista acumulada de forma destructiva para obtener el valor de retorno. Son bien conocidas otras técnicas para permitir agregar a una lista en tiempo constante, pero no siempre resultan ser más eficientes que el enfoque de antes de invertir.
En Clojure, puede utilizar transitorios para llegar allí, confiando en el comportamiento destructivo de la función conj!
:
(let [r (transient [])]
(dotimes [i 10]
(conj! r (* i i))) ;; destructive
(persistent! r))
que parece funcionar, pero the documentation on transients advierte que no se debe utilizar conj!
a " bash values in place "— es decir, contar con un comportamiento destructivo en lugar de capturar el valor de retorno. Por lo tanto, esa forma debe ser reescrita.
Con el fin de volver a enlazar r
anterior al nuevo valor producido por cada llamada a conj!
, tendríamos que utilizar un atom para introducir un nivel de indirección más.En ese punto, sin embargo, solo estamos luchando contra dotimes
, y sería mejor escribir su propio formulario usando loop
y recur
.
Sería bueno poder preasignar el vector para que sea del mismo tamaño que el límite de la iteración. No veo una manera de hacerlo.
¿Cuál es la última pregunta sobre esta pregunta? ¿Es clj-iterate la mejor solución o hay mejores alternativas? – jcheat