I have tried to come up with an implementation of quick sort in Common Lisp, and this is what I have got so far:
(defun quick-sort (list)
(if (cdr list)
(let ((pivot (car list)))
(append (quick-sort (remove-if-not (lambda (n) (< n pivot)) list))
(remove-if-not (lambda (n) (= n pivot)) list)
(quick-sort (remove-if-not (lambda (n) (> n pivot)) list))))
list))
Apparently it works, but I think that there is too much repetition in that code. Basically, we have three times:
(remove-if-not (lambda (n) (< n pivot)) list)
The only way the three calls differ is by >
vs =
vs <
.
Hence my question is: How could I remove that redundancy and make the code more readable and more compact?
Of course I could extract things to a function, such as:
(defun which (operator pivot list )
(remove-if-not (lambda (n) (funcall operator n pivot)) list))
(defun quick-sort (list)
(if (cdr list)
(let ((pivot (car list)))
(append (quick-sort (which #'< pivot list))
(which #'= pivot list)
(quick-sort (which #'> pivot list))))
list))
But somehow I'm not really convinced whether this is the best approach. It still feels clumsy to have to hand over pivot
and list
again and again.
I also had the idea to use flet
, which makes the actual body of the function more readable, but only moves the complexity to somewhere else:
(defun quick-sort (list)
(if (cdr list)
(let ((pivot (car list)))
(flet ((left () (remove-if-not (lambda (n) (< n pivot)) list))
(middle () (remove-if-not (lambda (n) (= n pivot)) list))
(right () (remove-if-not (lambda (n) (> n pivot)) list)))
(append (quick-sort (left))
(middle)
(quick-sort (right)))))
list))
Any other approaches?