next up previous
Next: 10 例題: データベース検索例 Up: 9 シーケンスデータ Previous: 9.9 find-all-if

9.10 find-all

findはひとつのデータを見つけ出すという関数でしたが, find-allという関数はあるデータと比較して関連するデータを すべて見つけ出す関数とします. CommonLispにはありませんが,以下にそれを定義します.
<cl> (find-all 3 '(2 -3.0 1 2 3 -3 4 5))
(3) 
<cl> (find-all 3 '(2 -3.0 1 2 3 -3 4 5) :key #'abs)
(3 -3) 
<cl> (find-all 3 '(2 -3.0 1 2 3 -3 4 5) :key #'abs
                                        :test #'equalp)
(-3.0 3 -3)
この関数find-allを作るためにはremoveを使うことができます.
<cl> (remove 3 '(2 -3.0 1 2 3 -3 4 5))
(2 -3.0 1 2 -3 4 5) 
<cl> (remove 3 '(2 -3.0 1 2 3 -3 4 5) :test #'equalp)
(2 -3.0 1 2 -3 4 5) 
<cl> (remove 3 '(2 -3.0 1 2 3 -3 4 5)
               :key #'abs :test #'equalp)
(2 1 2 4 5) 
<cl> (remove 3 '(2 -3.0 1 2 3 -3 4 5) :key #'abs
          :test #'(lambda (x y) (not (equalp x y))))
(-3.0 3 -3)
以下のようにfind-allを定義することができます.
(defun find-all (item sequence
                  &rest keyword-args
                  &key (test #'eql)
                  &allow-other-keys)
  (apply #'remove item sequence
         :test #'(lambda (&rest args)
                         (not (apply test args)))
         keyword-args))
ここで,&allow-other-keysというのは&keyによってキーワード引数として 宣言されている:test以外のキーワードを受け付けるためにつけなければ ならないものです.
(find-all 3 '(2 -3.0 1 2 3 -3 4 5)
              :key #'abs :test #'equalp)
とした場合には,&rest引数である keyword-argsには
(:key #'abs :test #'equalp)
が入っています.このkeyword-argsがapplyによってそのままremoveの引数として 渡されてしまいます.つまり,
(apply #'remove 3 '(2 -3.0 1 2 3 -3 4 5)
          :test #'(lambda (&rest args)
                         (not (apply #'equalp args)))
         (list :key #'abs :test #'equalp))
となって渡されたことになり,結局,
(remove 3 '(2 -3.0 1 2 3 -3 4 5)
         :test #'(lambda (&rest args)
                         (not (apply #'equalp args)))
         :key #'abs :test #'equalp)
と実行したことと同じになります.ここで,:testのキーワードが二つ渡され たことになりますが,最初のキーワードが優先されるようになっています.
<cl> (apply #'remove 3 '(2 -3.0 1 2 3 -3 4 5)
          :test #'(lambda (&rest args)
                          (not (apply #'equalp args)))
         (list :key #'abs :test #'equalp))
(-3.0 3 -3) 
<cl> (remove 3 '(2 -3.0 1 2 3 -3 4 5)
         :test #'(lambda (&rest args)
                         (not (apply #'equalp args)))
         :key #'abs :test #'equalp)
(-3.0 3 -3)
removeはシーケンスデータに対応した関数になっているため,find-allもシー ケンスデータに対応しています.しかし,シーケンスでない一般のリストデー タには適用できません.
<cl> (find-all #\3 "123456343")
"333" 
<cl> (find-all 3 '(2 -3.0 1 2 (3) -3 4 5)
                  :key #'abs :test #'equalp)
Error: non-number to abs: (3)

<cl> (find-all 3 '(2 -3.0 1 2 (3) -3 4 5)
                  :test #'equalp)
NIL


generated through LaTeX2HTML. M.Inaba 平成18年5月7日