git.fiddlerwoaroof.com
lexical-compare.lisp
45206306
 (defpackage :fwoar.lexical-compare
   (:use :cl )
1c201104
   (:export
    #:lexi-compare
    #:apply-when
    #:natural-sort-strings))
45206306
 (in-package :fwoar.lexical-compare)
 
 (defun parse-mixed-string (str)
   (let ((first-int-pos (position-if #'digit-char-p str)))
     (if (> (length str) 0)
         (if first-int-pos
             (if (> first-int-pos 0)
                 (cons (subseq str 0 first-int-pos)
                       (parse-mixed-string (subseq str first-int-pos)))
                 (multiple-value-bind (int end) (parse-integer str :junk-allowed t)
                   (cons int
                         (parse-mixed-string
                          (subseq str end)))))
             (list str))
         nil)))
 
 (defgeneric part< (a b)
   (:method (a b)
     nil)
   (:method ((a string) (b number))
     t)
   (:method ((a number) (b number))
     (< a b))
   (:method ((a string) (b string))
     (string< a b)))
 
 (defgeneric part= (a b)
   (:method (a b)
     nil)
   (:method ((a number) (b number))
     (= a b))
   (:method ((a string) (b string))
     (string= a b)))
 
cce5172b
 #+(or)
45206306
 (st:deftest test-parse-mixed-string ()
   (st:should be equal
              (list)
              (parse-mixed-string ""))
 
   (st:should be equal
              (list "asdf")
              (parse-mixed-string "asdf"))
 
   (st:should be equal
              (list "asdf" 1234)
              (parse-mixed-string "asdf1234"))
 
   (st:should be equal
              (list 1234 "asdf")
              (parse-mixed-string "1234asdf"))
 
   (st:should be equal
              (list "asdf" 1234 "a")
              (parse-mixed-string "asdf1234a")))
 
1c201104
 (defun apply-when (fun &rest args)
   (when (car (last args))
     (apply 'apply fun args)))
 
 (defun lexi-compare (a b &optional (elem-compare 'part<))
cce5172b
   (let* ((mismatch-pos (mismatch a b :test 'part=))
          (a-tail (when mismatch-pos (nthcdr mismatch-pos a)))
          (b-tail (when mismatch-pos (nthcdr mismatch-pos b))))
     (or (when (and a-tail b-tail)
           (funcall elem-compare
                    (car a-tail)
                    (car b-tail)))
         (null a-tail))))
1c201104
 
45206306
 (defun natural-sort-strings (a b)
   (lexi-compare (parse-mixed-string a)
                 (parse-mixed-string b)))