lisp中的布尔函子



我发现自己处于需要将几个谓词组合为一个谓词的情况。是否有类似compliment的标准方法?

假设有几个简单的谓词(例如is-fruit-p, is-red-p, grows-on-trees-p等)和一个对象列表,其中一个子集必须使用多个谓词过滤出来。有什么比下面这些更好的方法来实现这个目标呢?

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

您确定特殊的语法真的有用吗?考虑下面的

(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) ...)包装器。更重要的是,代码也将更难读取(除了必须学习用于谓词组合的特殊新宏)。

在我看来,如果你传递谓词并且需要将谓词传递给其他人,那么编写和/或组合器可能是有意义的…但不是为了编写示例中使用的代码;我要写

(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)

disjoinconjoin这样的高阶函数在quicklisp可安装的alexandria库中可用。

CL-USER> (ql:quickload "alexandria")
...
CL-USER> (remove-if (alexandria:disjoin #'zerop #'oddp #'minusp)
                    '(0 -1 1 -2 2))
=> (2)

我不确定包装盒中是否有这样的功能。如果需要组合可以在编译时间内确定的函数,则可以编写一个宏来完成此操作。如果你必须动态地检测谓词函数,你可以编写一个函数来做这件事,它将循环抛出函数列表并累积结果,直到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)

使用一级函数的方法:

(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)
(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)))

最新更新