5

I was writing a Sieve-type function in Clojure based on Sieve of Eratosthenes.....and came across an error with lists of pairs: ClassCastException clojure.lang.Cons cannot be cast to java.lang.Number  clojure.lang.Numbers.remainder (Numbers.java:171)


(defn mark-true [n]
  (cons n '(true)))

(defn unmarked? [ns]
  (not (list? ns)))

(defn divides? [m n]
  (if (= (mod n m) 0)
      true
      false ))

(defn mark-divisors [n ns]
  (cond 
      (empty? ns) '()
      (and (unmarked? (first ns)) (divides? n (first ns))) 
           (cons (cons (first ns) '(false)) (mark-divisors n (rest ns)))
      :else (cons (first ns) (mark-divisors n (rest ns)))))

(defn eratosthenes [ns]
  (cond 
      (empty? ns) '()
      (unmarked? (first ns))
           (cons (mark-true (first ns)) 
                 (eratosthenes (mark-divisors (first ns) (rest ns))))
      :else (cons (first ns) (eratosthenes (rest ns)))))

;(eratosthenes (list 2 3 4 5 6))
;=> ClassCastException clojure.lang.Cons cannot be cast to java.lang.Number  clojure.lang.Numbers.remainder (Numbers.java:171)

However, changing the marking style, giving up on cons and using conj or vector pairs instead, both solved the error.

Still I am looking for a good explanation of the error....

FredAKA
  • 1,248
  • 9
  • 8

1 Answers1

6

The problem is that list? check fails on a sequence built with cons as demonstrated below:

(list? (conj () 1)) ;=> true
(list? (cons 1 ())) ; => false

You could switch your call to list? to a call to seq? and it should work.

For details on why this is so I recommend reading this answer: Clojure: cons(seq) vs. conj(list)

Community
  • 1
  • 1
ponzao
  • 20,684
  • 3
  • 41
  • 58
  • Great. Thanks for solution. Funny list? is not discussed in reading ref. Your example should be an included there and http://clojuredocs.org/clojure_core/clojure.core/list_q – FredAKA Jan 24 '14 at 03:50
  • I think is a mistake in design to have (list? (cons 1 ())) ; => false – FredAKA Jan 25 '14 at 19:45
  • @FredAKA, you need to to take into account that there is a distinction between a list and a seq in Clojure. – ponzao Jan 25 '14 at 20:16
  • surely, dont need to "call" it a list and conflict with the well understood abstraction of that goes back to the first lisps built using cons – FredAKA Jan 26 '14 at 21:12