I'm new to Lisp and I'm yet to wrap my head around the Lisp way of writing programs. Any comments regarding approach, style, missed opportunities appreciated:
In particular, please advice if I build results list correctly ((setf merged-list (append merged-list (list a)))).
;;;; Count inversions
(defun small-list (list)
(or (null list) (null (rest list))))
(defun split-in-half (list)
(let ((mid (ceiling (length list) 2)))
(values (subseq list 0 mid)
(subseq list mid))))
(defun count-inversions (list)
(if (small-list list) (list list 0)
(multiple-value-bind (lower upper) (split-in-half list)
(merge-inversions
(count-inversions lower)
(count-inversions upper)))))
(defun merge-inversions (lower-pair upper-pair)
(let ((lower (first lower-pair))
(upper (first upper-pair))
(merged-list '())
(num-inversions 0))
(loop while (not (and (null lower) (null upper)))
do (cond
((null lower) (let ((a (first upper)))
(setf merged-list (append merged-list (list a)))
(setf upper (rest upper))))
((null upper) (let ((a (first lower)))
(setf merged-list (append merged-list (list a)))
(setf lower (rest lower))))
((< (first lower) (first upper)) (let ((a (first lower)))
(setf merged-list (append merged-list (list a)))
(setf lower (rest lower))) )
(t (let ((a (first upper)))
(setf merged-list (append merged-list (list a)))
(setf upper (rest upper))
(incf num-inversions (length lower))))))
(list merged-list (+ (second lower-pair) (second upper-pair) num-inversions)))
UPD: revised version using @sds's suggestions:
(defun small-list-p (list)
(or (null list) (null (rest list))))
(defun split-in-half (list)
(let ((mid (ceiling (length list) 2)))
(values (subseq list 0 mid)
(subseq list mid))))
(defun count-inversions (list)
(if (small-list-p list) (list list 0)
(multiple-value-bind (lower upper) (split-in-half list)
(merge-inversions
(count-inversions lower)
(count-inversions upper)))))
(defmacro move-last (source target)
`(setf ,target (nconc ,target (list (pop ,source)))))
Function MERGE-INVERSIONS:
(defun merge-inversions (lower-pair upper-pair )
(loop
with lower = (first lower-pair)
with upper = (first upper-pair)
with merged-list = '()
with num-inversions = 0
while (not (and (null lower) (null upper)))
do (cond
((null lower) (move-last upper merged-list))
((null upper) (move-last lower merged-list))
((< (first lower) (first upper)) (move-last lower merged-list))
(t
(move-last upper merged-list)
(incf num-inversions (length lower)) ))
finally (return (list merged-list
(+ (second lower-pair)
(second upper-pair)
num-inversions)))))
Performance went from this
139.740 seconds of real time
80,080,445,136 bytes consed
to this:
26.411 seconds of real time
87,009,536 bytes consed