:keyの活用 - lisp-cookbook-ja/common-lisp GitHub Wiki

Common Lispのシークエンスを扱う関数には:keyに関数を指定することで、読み出し方法を指定することが可能なことがあります。

:keyを活用すれば、オブジェクトのスロットの値によってソートする等のことが可能です。

また、標準以外のライブラリなどでも、この規約に沿ったものが多くあります。

:key が利用できるCL標準の関数は下記の通りです

adjoin, assoc, assoc-if, assoc-if-not, find, find-if, find-if-not, intersection, member, member-if, member-if-not, merge, nintersection, nset-difference, nset-exclusive-or, nsublis, nsubst, nsubst-if, nsubst-if-not, nunion, position, position-if, position-if-not, pushnew, rassoc, rassoc-if, rassoc-if-not, set-difference, set-exclusive-or, sort, stable-sort, sublis, subsetp, subst, subst-if, subst-if-not, union

コード例

下記の例では、オブジェクトのプロパティを:keyで拾うことにより、抽出、ソート、グループ化をしています。

(ユーザー定義の関数の例として、com.informatimago.common-lisp.list:equivalence-classesを利用しています。)

(import 'com.informatimago.common-lisp.list:equivalence-classes)

;;; データ(購入者名 商品名 購入日時(universal time))
(defclass item ()
  ((purchase-date :accessor purchase-date :initarg :purchase-date)
   (purchaser :accessor purchaser :initarg :purchaser)
   (product-name :accessor product-name :initarg :product-name)))

(defvar *items*
  (mapcar (lambda (p n d)
            (make-instance 'item
                           :purchaser p
                           :product-name n
                           :purchase-date d))
        '("おそ松" "カラ松" "チョロ松" "一松" "十四松" "トド松")
        '("テレビ" "テレビ" "エアコン" "ノートPC" "扇風機" "掃除機")
        '(3512674686 3556829851 3543518696 3526561150 3514854835 3529844173)))
;;; 期間でしぼりこみ
(defun between (fn start-ut end-ut items)
  (remove-if-not (lambda (i)
                   (<= start-ut i end-ut ))
                 items
                 :key fn))


;;; オブジェクトのスロットでソート
(defun order-by (fn items)
  (sort items #'< :key fn))


;;; オブジェクトのスロットでグループ化
(defun group-by (fn items)
  (equivalence-classes items :test #'string= :key fn))


;;; アイテムをプリント
(defun print-item (item)
  (let ((item (if (consp item) item (list item))))
    (format t "商品 = ~A~%" (product-name (car item)))
    (dolist (e item)
      (format t "~4T購入者は、~Aさん~%" (purchaser e)) )))



;;; 実行
(dolist (item (group-by #'product-name
                        (order-by #'purchase-date
                                  (between #'purchase-date
                                           (encode-universal-time 0 0 0 1 1 2011)
                                           (encode-universal-time 0 0 0 31 12 2013)
                                           *items*))))
  (print-item item))
;->  商品 = エアコン
;        購入者は、チョロ松さん
;    商品 = 掃除機
;        購入者は、トド松さん
;    商品 = ノートPC
;        購入者は、一松さん
;    商品 = 扇風機
;        購入者は、十四松さん
;    商品 = テレビ
;        購入者は、カラ松さん
;        購入者は、おそ松さん
;
;=>  NIL