Search code examples
functionclojuretail-recursiontail-call-optimization

Tail call optimization in Clojure


I have the following function.

   (defn get-all-str
      "Get a list of all strings of length n,
       consisting of characters from the alphabet list
       and not containing two identical characters in a row."
      [n alphabet]
      (letfn [(add-to-result [current-string result]
                (if (= (count current-string) n)
                  (conj result current-string)
                  (loop [remaining-alphabet (remove #(= % (last current-string)) alphabet)
                         acc result]
                    (if (empty? remaining-alphabet)
                      acc
                      (recur (rest remaining-alphabet)
                             (add-to-result (str current-string (first remaining-alphabet)) acc))))))]
        (if (<= n 0)
          []
          (add-to-result "" []))))

I'm using recur, but I still haven't done tail recursion since I'm calling add-to-result in the usual way. How do I rearrange this function to turn it into a tail recursion?

UPD:

For some reason stack overflow is deleting my thank you comments. I'll try to post here. Thank you all very much for your replies! You all helped me a lot to understand


Solution

  • When this algorithm is implemented using recursion, the partial result during computation is stored in the stack of the JVM. To express this algorithm without recursion using just a loop, that partial result needs to be stored elsewhere, for example in a loop variable that we call stack in the code example below:

    (defn loop-get-all-str [n alphabet]
      (let [alphabet (set alphabet)]
        (loop [[s & stack] [""]
               result []]
          (if s
            (if (= n (count s))
              (recur stack (conj result s))
              (recur (into stack
                           (map (partial str s))
                           (disj alphabet (last s)))
                     result))
            result))))