Sunday, January 24, 2010

A Little Bit of Clojure and Scheme

Following my desire to read as much SICP as I can without going nuts, I did at least one SICP exercise (Exercise 1.8) with both Scheme and Clojure.

I'm putting them here for a lightweight comparison. They're not supposed to be efficient or clever solutions, just the first ones I came up with. Since there are really few differences between the two languages at this level, I took the liberty of using Clojure thread-first macro to be able to contrast something. Thanks to Sean Devlin screencast on the subject.

Scheme
(define (square x)
     (* x x))
(define cube
     (lambda (x)
     (* x x x)))
(define (cubert x)
   (cubert-iter 1 x))
(define (cubert-iter guess x)
   (if (good-enough? guess x)
       guess
       (cubert-iter (improve guess x)
                    x)))
(define (good-enough? guess x)
   (< (abs (- (cube guess) x))
          .0001))
(define (improve y x)
   (/ (+ (/ x
            (square y))
         (* 2.0 y))
       3.0))

Clojure
(defn square [x]
     (* x x)) ;I prefer fn than using the #() macro
 (def cube
   (fn [x]
    (* x x x)))
  (defn cubert [x]
   (cubert-iter 1 x)) ;Need to reorder before usage or "declare" beforehand
  (def good-enough?)
  (def improve)
  (defn cubert-iter [guess x]
   (if (good-enough? guess x)
       guess
       (cubert-iter (improve guess x)
                  x)))
  (defn good-enough? [guess x]
   (< (Math/abs (- (cube guess) x))
          0.0001)) ;Uses the thread-first macro to make it a bit different
 (defn improve [y x]
   (-> x
     (/ (square y))
     (+ (* 2.0 y))
     (/ 3.0)))

2 comments:

fristname said...

Wow didn't know about the screencast!
Thx for sharing!

Michel S. said...

You might want to use recur for tail recursion for the Clojure examples, otherwise you will blow the stack, given a high enough number of iterations :)

We keep hearing that the JVM will eventually include proper support for tail calls, but it has not happened yet. And Rich (Hickey) took a more risk-averse solution -- with recur -- than the Scala folks, who try and optimize tail-calls for you seamlessly (though now they added @tailrec for verification)