This is a read-only archive!

Playing with Lisp (Ruby quiz 138)

Having finished reading PCL I decided to try out some Common Lisp. I wrote a solution to Ruby quiz 138 in Lisp. I figured doing a Ruby quiz is a good way to compare languages. I did this without looking at other people's solutions beforehand.

My Lisp is very clumsy and full of repetition that can probably be abstracted away if I knew what I was doing. But it does give me the right answer, which is good, and took less than an hour to write, which is also good. Interestingly, some things that are really easy in Ruby due to being included in the standard libraries, like splitting strings and joining arrays of strings, are not that trivial in Lisp. And some things that aren't in the standard libraries in Ruby, namely converting integers to an English-language representation, are included in the standard libraries in Lisp.

I got tripped up in my Lisp solution because I'm not familiar enough with alists vs. plists vs. other list-based structures vs. hashes. I originally tried to use hash tables for everything, because that's how I'd do it in Ruby, but I couldn't figure out how to sort a hash table and flatten it into a vector or list. I could've rolled my own sort routine or hash->list function but I figured it's probably easier to use something like alists. I think I was wrong, but such is life.

I got a small taste of playing with Emacs and Slime while giving this a go. It's not so bad after all, I guess. C-c C-q closes open parens in a form, which is a lifesaver. C-c C-c compiles the form under the cursor, and C-c C-k compiles a whole file worth of forms. So find myself banging out a function in an Emacs file buffer, C-c C-c compiling it, hopping to the REPL in another buffer and trying out my new function, and if it doesn't work, tweaking it and recompiling and trying it again. Being able to selectively compile one form at a time is nice. Being able to do everything I need to do without leaving Emacs at all is also nice. Still getting used to many of the other Emacs shortcuts though. Vim is really ingrained in my mind. Is it possible to become proficient in both? I wonder.

Either way, my Lisp is really bad and I'm clearly still thinking in Ruby. But it's a start.

(defun split-into-words (sentence)
    ((consp sentence) sentence)
    ((stringp sentence)
     (loop for n = 0 then (+ m 1)
       for m = (position #\Space sentence :start n)
       collect (subseq sentence n m)
       while m))))

(defun join-into-sentence (counts)
  (format nil "~{~a~^ ~}" 
          (loop for pair in counts 
                collect (remove #\- (format nil "~r ~a" (cdr pair) (car pair))))))

(defun sort-counts (counts &key (sort-fn 'string<))
  (sort counts #'(lambda (x y) (funcall sort-fn (car x) (car y)))))

(defun count-letters (sentence)
  (let ((counts))
    (loop for word in sentence
          collect (loop for letter across word
                do (if (assoc letter counts :test 'equal)
                   (setf (cdr (assoc letter counts :test 'equal))
                     (1+ (cdr (assoc letter counts :test 'equal))))
                   (push (cons letter 1) counts)))
          finally (return counts))))

(defun look-and-say (sentence)
  (join-into-sentence (sort-counts (count-letters (split-into-words sentence)))))

(defun loop-look-and-say (sentence &key (max-runs 1000))
  (do* ((sentence sentence (look-and-say sentence))
    (number-of-runs 0 (1+ number-of-runs))
    (seen-sentences (make-hash-table :test 'equal)))
       ((or (gethash sentence seen-sentences) 
        (> number-of-runs max-runs)) 
    (format nil "~a runs:~% ~a" number-of-runs sentence))
    (setf (gethash sentence seen-sentences) t)))
September 21, 2007 @ 3:56 PM PDT
Cateogory: Programming
Tags: Lisp, Ruby, Emacs