Non sono sicuro del motivo per cui si tratta di "creare e buttare via" la sequenza lenta creata dalla funzione range
. L'iterazione limitata effettuata da dotimes
è probabilmente più efficiente, poiché rappresenta un incremento in linea e viene confrontato con ogni passaggio, ma è possibile che si paghi un costo aggiuntivo per esprimere la propria concatenazione di elenchi.
La soluzione Lisp tipica è quella di anteporre nuovi elementi a una lista che si costruisce mentre si procede, quindi di invertire la lista costruita in modo distruttivo per ottenere il valore restituito. Altre tecniche per consentire l'aggiunta a un elenco in tempo costante sono ben note, ma non sempre si dimostrano più efficienti rispetto all'approccio anteporre-retro-inverso.
In Clojure, è possibile utilizzare transitori per arrivarci, basandosi sul comportamento distruttivo della funzione conj!
:
(let [r (transient [])]
(dotimes [i 10]
(conj! r (* i i))) ;; destructive
(persistent! r))
che sembra funzionare, ma the documentation on transients avverte che non si dovrebbe usare conj!
a " valori bash in atto "— vale a dire contare su comportamenti distruttivi invece di prendere il valore restituito. Quindi, quel modulo deve essere riscritto.
Al fine di associare nuovamente r
sopra al nuovo valore dato da ogni chiamata a conj!
, avremmo bisogno di utilizzare un atom per introdurre più di un livello di indirezione.A quel punto, però, stiamo solo combattendo contro lo dotimes
, e sarebbe meglio scrivere il tuo modulo usando loop
e recur
.
Sarebbe bello poter preallocare il vettore in modo che abbia le stesse dimensioni del limite di iterazione. Non vedo un modo per farlo.
Qual è l'ultima risposta a questa domanda? Clj-iterate la soluzione migliore o ci sono alternative migliori? – jcheat