Thursday, July 24, 2008

Quicksort in Lisp

Version #1:
(defun generate-lt (value)
(lambda (x) (and (< x value) (list x))))

(defun generate-eq (value)
(lambda (x) (and (eq x value) (list x))))

(defun generate-gt (value)
(lambda (x) (and (> x value) (list x))))

(defun quicksort (list)
(if (<= (length list) 1)
list
(let ((pivot (nth (truncate (/ (length list) 2.0)) list)))
(append (quicksort (mapcan (generate-lt pivot) list))
(mapcan (generate-eq pivot) list )
(quicksort (mapcan (generate-gt pivot) list))))))
Duplication of code in the 'generate-' functions. Need a macro.

Version #2:
(defmacro generate-comparator (value fn)
`(lambda (x) (and (,fn x ,value) (list x))))

(defun quicksort (list)
(if (<= (length list) 1)
list
(let ((pivot (nth (truncate (/ (length list) 2.0)) list)))
(append (quicksort (mapcan (generate-comparator pivot <) list))
(mapcan (generate-comparator pivot eq) list )
(quicksort (mapcan (generate-comparator pivot >) list))))))
Looks elegant, but can we make this even more concise?

Version #3:
(defun quicksort (list)
(if (<= (length list) 1)
list
(let ((pivot (nth (truncate (/ (length list) 2.0)) list)))
(append (quicksort (mapcan (lambda (x) (and (< x pivot) (list x))) list))
(mapcan (lambda (x) (and (eq x pivot) (list x))) list)
(quicksort (mapcan (lambda (x) (and (> x pivot) (list x))) list))))))
Seven lines of condensed confusion. Not to mention wreaking havoc with the layout of the blog.

(Blog post inspired by a) a rekindled interest in Lisp and b) a sudden urge to share the joy of having found a non-gratuitous use for macros)

Update: Version #4:
(defun quicksort (list)
(if (<= (length list) 1)
list
(let ((pivot (first list)))
(nconc (quicksort (remove-if #'(lambda (x) (>= x pivot)) list))
(remove-if #'(lambda (x) (not (= x pivot))) list)
(quicksort (remove-if #'(lambda (x) (<= x pivot)) list))))))