Сортировка в естественном порядке для Emacs Lisp

Кто-нибудь реализовал сортировку в естественном порядке в Emacs Lisp? Я знаю, что писать несложно, но проще позаимствовать чужую работу.

(Да, я не могу поверить, что только что искал функцию Emacs и не смог ее найти.)


person Ken    schedule 21.12.2009    source источник


Ответы (1)


Этот код предоставляет 'dictionary-lessp, который можно использовать в алгоритмах сортировки. Кажется, работает в моем тестировании до сих пор:

(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))))

Это мое тестирование:

(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")))

Пример использования:

(sort '("b" "a" "1" "f19" "f" "f2" "f1can") 'dictionary-lessp)

урожаи

("1" "a" "b" "f" "f1can" "f2" "f19")
person Trey Jackson    schedule 21.12.2009
comment
Это потрясающе, спасибо! P.S., я думаю, вы забыли ;; до p != 0. - person Ken; 22.12.2009
comment
Я не понимаю комментарий, о каком чеке вы говорите? - person Trey Jackson; 22.12.2009
comment
Текст p != 0 выглядит как комментарий (и не имеет смысла в COND), но перед ним нет маркера комментария. - person Ken; 22.12.2009