ハッシュを値で降順、値が等しい場合キーで昇順にソートする - lisp-cookbook-ja/common-lisp GitHub Wiki

ハッシュ

ハッシュを値で降順、値が等しい場合キーで昇順にソートする

Common Lispのハッシュテーブルは順番付きハッシュテーブルではありませんので、ハッシュテーブルの要素がソート可能である必要がありません。

「ソートされたハッシュ」は存在しませんが、plistなどの形で出力することは可能です。

ライブラリ:Alexandriaを利用

(defun hash-table-top-n-values (table predicate &key (key #'car))
  "Returns sorted entries from hash table TABLE."
  (sort (alexandria:hash-table-alist table) predicate :key key))

実行

(hash-table-top-n-values (alexandria:alist-hash-table
                          (pairlis '(1 2 3 4 5 6) '(b a b d e c)))
                         #'<
                         :key #'car)
;=>  ((1 . B) (2 . A) (3 . B) (4 . D) (5 . E) (6 . C))

標準のsortには比較値が等しかった場合の挙動の規定はなく値が等しいときにキーで昇順にソートはライブラリを利用するか自作することになるでしょう。

;;; Perl/Rubyの<=>演算子風。等しい場合は、0
;;;  第一引数の方が小さい場合には負値、大きい場合は正値を返す

(defmethod <=> ((x string) (y string) &optional (factor 1))
  (* (cond ((string= x y) 0)
           ((string< x y) -1)
           (T 1))
     factor))


(defmethod <=> ((x symbol) (y symbol) &optional (factor 1))
  (* (cond ((string= x y) 0)
           ((string< x y) -1)
           (T 1))
     factor))


(defmethod <=> ((x number) (y number) &optional (factor 1))
  (* (cond ((= x y) 0)
           ((< x y) -1)
           (T 1))
     factor))


(defun <=>sort (sequence predicate &key (key #'values))
  (sort sequence (lambda (x y)
                   (<= 0 (funcall predicate 
                                  (funcall key x)
                                  (funcall key y))))
        :key key))

実行

(defvar *table* (alexandria:alist-hash-table 
                 '((6 . C) (5 . E) (4 . D) (3 . B) (2 . A) (1 . B))))


;;; 値は降順、キーは昇順 ((キー . 値) ...)
(<=>sort (alexandria:hash-table-alist *table*)
         (lambda (X Y) 
           (+ (<=> (cdr X) (cdr Y) 2) ;キーを優先(降順)
              (<=> (car Y) (car X)))))
;=>  ((5 . E) (4 . D) (6 . C) (1 . B) (3 . B) (2 . A))


;;; 値、キーともに降順 
(<=>sort (alexandria:hash-table-alist *table*)
         (lambda (x y) 
           (+ (<=> (cdr x) (cdr y) 2) ;キーを優先(降順)
              (<=> (car x) (car y)))))
;=>  ((5 . E) (4 . D) (6 . C) (3 . B) (1 . B) (2 . A))