8

What is the Clojure equivalent (for the exact algorithm) of the following Python code?

from itertools import count
from math import sqrt

def prime_gen():
   primes = []
   for n in count(2):
       if all(n%p for p in primes if p <= sqrt(n)):
           primes.append(n)
           yield n
Roskoto
  • 1,567
  • 1
  • 14
  • 27

3 Answers3

11

This is as Pythonish as I can make it:

(def prime-gen
     (let [primes (atom [])]
       (for [n (iterate inc 2)
             :when (not-any? #(zero? (rem n %))
                             (filter #(<= % (Math/sqrt n)) 
                                     @primes))]
         (do (swap! primes conj n)
             n))))

(take 10 prime-gen)  ; => (2 3 5 7 11 13 17 19 23 29)

Clojure doesn't consider the integer 0 to be boolean false. It took me a few minutes to figure out that your Python code was taking advantage of this.

Here are some other prime number algorithms in Clojure. There's also a prime number implementation in clojure.contrib.lazy-seqs.

Community
  • 1
  • 1
Brian Carper
  • 66,618
  • 25
  • 158
  • 164
  • It doesn't need to be Pitonish :) If there is a more idiomatic clojure solution for the same algorithm, please send it too. – Roskoto Oct 19 '09 at 20:51
  • 2
    You should follow the link. There are many examples and answers there. Also http://clj-me.cgrand.net/2009/07/30/everybody-loves-the-sieve-of-eratosthenes/#comments. – dnolen Oct 19 '09 at 21:10
  • 1
    Well, it's not all that un-Clojurish other than mutating an `atom`. It'd take some contortions to avoid using an `atom` though. Some algorithms demand side-effects and a non-functional programming style (especially things like sorting-in-place, shuffling, specific mathematical functions etc.) and it's OK to switch to using mutable data structures in those cases. That's why Clojure makes them available. You could even dive down and use native Java data structures for these kinds of things. – Brian Carper Oct 19 '09 at 21:15
0

This version is much faster than @Brian Carper's

(def prime-gen
  (let [primes (atom [2N])]
    (iterate
      #(let [ps @primes]
         (loop [n (inc %)]
           (if (loop [i 0]
                 (let [p (nth ps i)]
                   (cond
                     (< n (* p p)) true
                     (zero? (mod n p)) false
                     :else (recur (inc i)))))
             (do (swap! primes conj n) n)
             (recur (inc n)))))
      (first @primes))))
SaltyEgg
  • 1,368
  • 1
  • 10
  • 23
0

Here is the algorithm in fairly idiomatic Clojure. I've tried to keep the names the same so that you can see how this code corresponds.

(def all? (partial every? identity))

(defn prime-gen []
  (let [unfold
        (iterate
          (fn step [[primes n]]
            (if (all?
                  (for [p primes :when (<= p (Math/sqrt n))]
                    (not= 0 (mod n p))))
              [(conj primes n) (inc n)]
              (recur [primes (inc n)])))
          [[] 2])]
    (map (comp peek first) (rest unfold))))

Every iteration of step

  • appends the next prime to the first component of its argument and
  • hands on the next candidate prime in the second component.

The last line picks out the appended primes from the iterations.

It works:

(take 11 (prime-gen))
=> (2 3 5 7 11 13 17 19 23 29 31)

We can also see some inefficiency: The :when (<= p (Math/sqrt n) clause (and its Python equivalent if p <= sqrt(n)) are useless. We still go through all the discovered primes instead of stopping when they get too big to be possible factors. To mend this,

  • in Clojure, we replace the :when with a :while;
  • in Python, I think we wrap primes in a takewhile instead of following it with an if (untested).

Even so, the algorithm is slow. For something faster, see lazy-primes3 from Christophe Grand's blog on the subject.

Thumbnail
  • 12,695
  • 2
  • 24
  • 36