Natural order sort for Emacs Lisp
Has anyone implemented a natural order sort in Emacs Lisp? I know it's not hard to write, but it's easier to borrow someone else's work.
(Yeah, I can't believe I just searched开发者_运维知识库 for an Emacs function and couldn't find it.)
This code provides a 'dictionary-lessp
which can be used in sorting algorithms. Seems to work in my testing so far:
(defun dictionary-lessp (str1 str2)
"return t if STR1 is < STR2 when doing a dictionary compare
(splitting the string at numbers and doing numeric compare with them)"
(let ((str1-components (dict-split str1))
(str2-components (dict-split str2)))
(dict-lessp str1-components str2-components)))
(defun dict-lessp (slist1 slist2)
"compare the two lists of strings & numbers"
(cond ((null slist1)
(not (null slist2)))
((null slist2)
nil)
((and (numberp (car slist1))
(stringp (car slist2)))
t)
((and (numberp (car slist2))
(stringp (car slist1)))
nil)
((and (numberp (car slist1))
(numberp (car slist2)))
(or (< (car slist1) (car slist2))
(and (= (car slist1) (car slist2))
(dict-lessp (cdr slist1) (cdr slist2)))))
(t
(or (string-lessp (car slist1) (car slist2))
(and (string-equal (car slist1) (car slist2))
(dict-lessp (cdr slist1) (cdr slist2)))))))
(defun dict-split (str)
"split a string into a list of number and non-number components"
(save-match-data
(let ((res nil))
(while (and str (not (string-equal "" str)))
(let ((p (string-match "[0-9]*\\.?[0-9]+" str)))
(cond ((null p)
(setq res (cons str res))
(setq str nil))
((= p 0)
(setq res (cons (string-to-number (match-string 0 str)) res))
(setq str (substring str (match-end 0))))
(t
(setq res (cons (substring str 0 (match-beginning 0)) res))
(setq str (substring str (match-beginning 0)))))))
(reverse res))))
This is my testing:
(and (dictionary-lessp "a" "b")
(null (dictionary-lessp "b" "a"))
(null (dictionary-lessp "a" "a"))
(dictionary-lessp "1" "2")
(null (dictionary-lessp "2" "1"))
(null (dictionary-lessp "1" "1"))
(dictionary-lessp "1" "a")
(null (dictionary-lessp "a" "1"))
(dictionary-lessp "" "a")
(null (dictionary-lessp "a" ""))
(dictionary-lessp "ab12" "ab34")
(dictionary-lessp "ab12" "ab123")
(dictionary-lessp "ab12" "ab12d")
(dictionary-lessp "ab132" "ab132z")
(dictionary-lessp "132zzzzz" "ab132z")
(null (dictionary-lessp "1.32" "1ab")))
Example usage is:
(sort '("b" "a" "1" "f19" "f" "f2" "f1can") 'dictionary-lessp)
yields
("1" "a" "b" "f" "f1can" "f2" "f19")
精彩评论