I'm writing a program that uses classical cons pairs a la Common Lisp, Scheme, et al.
(deftype Cons [car cdr]
clojure.lang.ISeq
(first [c] (.car c))
(more [c] (.cdr c))
I create lists by chaining cons cells, e.g. (Cons. a (Cons. b nil))
for the list containing a
and b
. I wrote a function to convert a Clojure collection into a cons list:
(defn conslist [xs]
(if (empty? xs)
nil
(Cons. (first xs) (conslist (rest xs)))))
This works but will overflow if xs
is too big. recur
doesn't work because the recursive call isn't in a tail position. Using loop
with an accumulator wouldn't work, because cons
only puts stuff at the front, when each recurse gives you the next item, and I can't use conj
.
What can I do?
Edit: In the end, it turns out if you get this working, Clojure fundamentally isn't designed to support cons pairs (you can't set the tail to a non-seq). I ended up just creating a custom data structure and car/cdr functions.
lazy-seq
is your friend here. It takes a body that evaluates to an ISeq, but the body will not be evaluated until the result of lazy-seq
is invoked.
(defn conslist [xs]
(if (empty? xs)
nil
(lazy-seq (Cons. (first xs) (conslist (rest xs))))))