5

我发现自己处于需要将多个谓词合并为一个的情况。是否有这样做的标准方法,类似于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 形式,那么使用组合器您将编写比不使用它更多的代码,因为(lambda (x) ...)每个参数项都需要包装器。更重要的是,代码也将更难阅读(除了必须学习一个特殊的新宏来进行谓词组合)。

IMO 如果您传入谓词并且需要将谓词传递给其他人,那么编写和/或组合器可能是有意义的......但不适用于编写您在示例中使用的代码;为此我会写

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

少写,少读,无需额外学习,参数化微不足道。

例如,假设您想要一份与您拥有的 (in 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

quicklisp可安装的alexandria库中提供了高阶函数,如disjoin和。conjoin

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

我不确定盒子里是否有这样的功能。如果您需要组合可以在编译时确定的函数,您可以编写一个宏来执行此操作。如果您必须动态检测谓词函数,您可以编写函数来执行此操作,该函数将循环抛出函数列表并累积结果直到出现错误条件。

宏可以如下所示:

(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 回答
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 回答
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 回答