5

いくつかの述語を 1 つに結合する必要がある状況に陥っています。これを行う標準的な方法はありcomplimentますか?

いくつかの単純な述語 ( is-fruit-p、など) とis-red-pgrows-on-trees-p複数の述語を使用してサブセットを除外する必要があるオブジェクトのリストがあるとします。これを達成するためのより良い方法は次のとおりです。

(remove-if #'is-fruit-p 
           (remove-if #'is-red-p 
                      (remove-if #'grows-on-trees-p list-of-objects)))
4

5 に答える 5

5

特別な構文が本当に役立つと確信していますか? 次のことを考慮してください

(lambda (x)
  (and (is-fruit-p x)
       (or (grows-on-tree-p x)
           (is-red-p x))))

そして今、もう少し一般的です

(lambda (x)
  (and (is-fruit-p x)
       (or (grows-on-tree-p x)
           (eq (color x) 'red))))

また

(lambda (x)
  (and (is-fruit-p x)
       (or (grows-on-tree-p x)
           (eq (color x) desired-color)))) ; desired-color captured lexical

述語用の特別な構文を作成したとしても、追加された言語の複雑さは、得られる剛性に見合う価値があると思いますか? たとえば、述語を定義します#'weights-exactly-five-ounces-pか? どう#'weights-up-to-and-including-six-and-half-ounces-pですか?

パラメトリック述語が必要になり始め、そのためにラムダ形式を使用する場合、コンバイナーを使用すると、使用しない場合よりも多くのコードを記述します。これは、(lambda (x) ...)パラメトリック用語ごとにラッパーが必要になるためです。さらに重要なことに、そのコードは読みにくくなります(述語の組み合わせのために特別な新しいマクロを学ばなければならないことに加えて)。

IMO 述語を渡され、述語を他の人に渡す必要がある場合は、および/またはコンバイナを作成することは理にかなっているかもしれませんが、例で使用したコードを作成する場合はそうではありません。そのために私は書くだろう

(remove-if (lambda (x) (or (is-fruit-p x)
                           (is-red-p x)
                           (grows-on-trees-p x)))
           list-of-objects)

書くことも読むことも少なく、学ぶべきことは何もなく、パラメータ化するのは簡単です。

たとえば、あなたが持っているものと同じ色(mine)で、同じ重さまたはおそらくそれより重い果物のリストが必要だとします...

(remove-if-not (lambda (x) (and (is-fruit-p x)
                                (eq (color x) (color mine))
                                (>= (weight x) (weight mine))))
               objects)
于 2013-03-18T13:40:31.257 に答える
4

disjoinやのような高階関数conjoinは、quicklispのインストール可能なalexandriaライブラリで利用できます。

CL-USER> (ql:quickload "alexandria")
...
CL-USER> (remove-if (alexandria:disjoin #'zerop #'oddp #'minusp)
                    '(0 -1 1 -2 2))
=> (2)
于 2013-03-18T21:39:51.673 に答える
3

ファーストクラス関数を使用したアプローチ:

(defun complement (func)
  (lambda (x) (not (funcall func x))))

(defun conjoin (pred1 pred2)
  (lambda (x) (and (funcall pred1 x) (funcall pred2 x))))

(defun disjoin (pred1 pred2)
  (lambda (x) (or (funcall pred1 x) (funcall pred2 x))))

あなたが生産できるもの

(remove-if (conjoin #'is-fruit-p (conjoin #'is-red-p #'grows-on-trees-p)) list-of-objects)
于 2013-03-18T14:13:37.610 に答える
3

そのような機能がボックスから利用できるかどうかはわかりません。コンパイル時に決定できる関数を組み合わせる必要がある場合は、これを行うマクロを作成できます。述語関数を動的に検出する必要がある場合は、これを行う関数を記述して、関数のリストをループスローし、false 条件になるまで結果を蓄積することができます。

マクロは次のようになります。

(defmacro combine-predicates (combine-func &rest preds)
  (let ((x (gensym)))
    `(lambda (,x) (,combine-func ,@(loop for p in preds 
                      collecting `(funcall ,p ,x))))))

そして、あなたはそれをこのように使うことができます

(remove-if (combine-predicates and 
                               #'is-fruit-p 
                               #'is-red-p 
                               #'grows-on-trees-p) obj-list)
于 2013-03-18T12:06:22.143 に答える
2
(let ((predicates '(zerop evenp)))
  (remove-if (lambda (item)
               (some (lambda (fn) (funcall fn item))
                     predicates))
             '(0 1 2 3 4 0 1 2 3 4)))
于 2013-03-18T13:01:51.793 に答える