The Clojure way of doing it would be to construct a regular list in reverse, adding the new element to the head of the list. This way, all of your operations only affect the (new) head node and you don't need to consume the stack. Then, at the end, you just reverse the (singly-linked list):
(dotest
(let [result-1 (loop [cum nil
val 0]
(if (< val 100000)
(recur
(cons val cum)
(inc val))
cum))
result-2 (reverse result-1)]
(is= (take 10 result-1)
[99999 99998 99997 99996 99995 99994 99993 99992 99991 99990])
(is= (take 10 result-2)
[0 1 2 3 4 5 6 7 8 9])))
If you really, really wanted, you could re-cast this using your ListNode
structure if you wanted (but, why?).
Having said that, if you want to build the list in-order, without reversing anything, just use a Java LinkedList:
(ns xxx
(:import [java.util LinkedList]))
(dotest
(let [linked-list (LinkedList.) ]
(dotimes [i 100000]
(.add linked-list i) )
(is= (take 10 linked-list)
[0 1 2 3 4 5 6 7 8 9])
(is= (take-last 10 linked-list)
[ 99990 99991 99992 99993 99994 99995 99996 99997 99998 99999 ] ) ))
You can also make a lazy-list. My favorite way is by using lazy-cons
:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn lazy-nodes
[value limit]
(when (< value limit )
(lazy-cons value (lazy-nodes (inc value) limit)) ))
(dotest
(let [result (lazy-nodes 0 100000)]
(is= (take 10 result)
[ 0 1 2 3 4 5 6 7 8 9] )
(is= (take-last 10 result)
[ 99990 99991 99992 99993 99994 99995 99996 99997 99998 99999 ] ) ))