Search code examples
listclojurefunctional-programminglisp

Concatenate elements to list by using loop in clojure?


I am trying to get into Lisps and FP by trying out the 99 problems.

Here is the problem statement (Problem 15)

Replicate the elements of a list a given number of times.

I have come up with the following code which simply returns an empty list []

I am unable to figure out why my code doesn't work and would really appreciate some help.

(defn replicateList "Replicates each element of the list n times" [l n]
  (loop [initList l returnList []]
    (if (empty? initList)
      returnList
      (let [[head & rest] initList]
        (loop [x 0]
          (when (< x n)
            (conj returnList head)
            (recur (inc x))))
      (recur rest returnList)))))

(defn -main
  "Main" []
  (test/is (=
           (replicateList [1 2] 2)
           [1 1 2 2])
          "Failed basic test")
  )

Solution

  • Here is a version based on the original post, with minimal modifications:

    ;; Based on the original version posted
    (defn replicateList "Replicates each element of the list n times" [l n]
        (loop [initList l returnList []]
        (if (empty? initList)
            returnList
            (let [[head & rest] initList]
            (recur
                rest
                (loop [inner-returnList returnList
                    x 0]
                (if (< x n)
                    (recur (conj inner-returnList head) (inc x))
                    inner-returnList)))))))
    

    Please keep in mind that Clojure is mainly a functional language, meaning that most functions produce their results as a new return value instead of updating in place. So, as pointed out in the comment, the line (conj returnList head) will not have an effect, because it's return value is ignored.

    The above version works, but does not really take advantage of Clojure's sequence processing facilities. So here are two other suggestions for solving your problem:

    ;; Using lazy seqs and reduce
    (defn replicateList2 [l n]
        (reduce into [] (map #(take n (repeat %)) l)))
    
    ;; Yet another way using transducers
    (defn replicateList3 [l n]
        (transduce
        (comp (map #(take n (repeat %)))
                cat
                )
        conj
        []
        l))
    

    One thing is not clear about your question though: From your implementation, it looks like you want to create a new list where each element is repeated n times, e.g.

    playground.replicate> (replicateList [1 2 3] 4)
    [1 1 1 1 2 2 2 2 3 3 3 3]
    

    But if you would instead like this result

    playground.replicate> (replicateList [1 2 3] 4)
    [1 2 3 1 2 3 1 2 3 1 2 3]
    

    the answer to your question will be different.