Benz
Benz

Reputation: 289

collect function of Lisp in Clojure

I defined a function is-prime? in Clojure that returns if a number is prime or not, and I am trying to define a function prime-seq that returns all the prime numbers between two numbersn and m.

I have created the function in Common Lisp since I am more comfortable with it and I am trying to translate the code to Clojure. However, I cannot find how to replace the collect function in Lisp to Clojure.

This is my prime-seq function in Lisp:

 (defun prime-seq (i j) 
    (loop for x from i to j
                when (is-prime x)
                collect x
     )
 )

And this is the try I did in Clojure but it is not working:

 (defn prime-seq? [n m]
   (def list ())
   (loop [k n]
     (cond 
       (< k m) (if (prime? k) (def list (conj list k)))

     )
   )
   (println list)

 )

Any ideas?

Upvotes: 0

Views: 304

Answers (2)

l0st3d
l0st3d

Reputation: 2968

loop in Clojure is not the same as CL loop. You probably want for:

(defn prime-seq [i j]
  (for [x (range i j)
        :when (is-prime x)]
    x))

Which is basically the same as saying:

(defn prime-seq [i j]
  (filter is-prime (range i j)))

which may be written using the ->> macro for readability:

(defn prime-seq [i j]
  (->> (range i j)
       (filter is-prime)))

However, you might actually want a lazy-sequence of all prime numbers which you could write with something like this:

(defonce prime-seq
  (let [c (fn [m numbers] (filter #(-> % (mod m) (not= 0)) numbers))
        f (fn g [s]
            (when (seq s)
              (cons (first s)
                    (lazy-seq (g (c (first s) (next s)))))))]
    (f (iterate inc 2))))

The lazy sequence will cache the results of the previous calculation, and you can use things like take-while and drop-while to filter the sequence.

Also, you probably shouldn't be using def inside a function call like that. def is for defining a var, which is essentially global. Then using def to change that value completely destroys the var and replaces it with another var pointing to the new state. It's something that's allowed to enable iterative REPL based development and shouldn't really be used in that way. Var's are designed to isolate changes locally to a thread, and are used as containers for global things like functions and singletons in your system. If the algorithm you're writing needs a local mutable state you could use a transient or an atom, and define a reference to that using let, but it would be more idiomatic to use the sequence processing lib or maybe a transducer.

Loop works more like a tail recursive function:

(defn prime-seq [i j]
  (let [l (transient [])]
    (loop [k i]
      (when (< k j)
        (when (is-prime k)
          (conj! l k))
        (recur (inc k))))
    (persistent! l)))

But that should be considered strictly a performance optimisation. The decision to use transients shouldn't be taken lightly, and it's often best to start with a more functional algorithm, benchmark and optimise accordingly. Here is a way to write the same thing without the mutable state:

(defn prime-seq [i j]
  (loop [k i
         l []]
    (if (< k j) 
      (recur (inc k)
             (if (is-prime k)
               (conj l k)
               l))
      l)))

Upvotes: 2

coredump
coredump

Reputation: 38799

I'd try to use for:

(for [x (range n m) :when (is-prime? x)] x)

Upvotes: 2

Related Questions