Search code examples
recursionclojuretail-recursioncons

Recursively building up cons lists


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.


Solution

  • 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))))))