lisp中的布尔函子

时间:2013-03-18 11:31:32

标签: lambda lisp common-lisp functor higher-order-functions

当我需要将多个谓词合并为一个时,我发现自己陷入了困境。有没有一种标准的方法,类似于compliment

假设有几个简单谓词(例如is-fruit-pis-red-pgrows-on-trees-p等)以及必须使用多个谓词过滤掉子集的对象列表。实现这一目标的最佳方法是:

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

5 个答案:

答案 0 :(得分: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)

写作少,阅读少,学习不多,参数化程度微不足道。

假设您想要一个水果列表,其颜色与您拥有的水果相同(在mine中)并且具有相同的重量或可能更重......

(remove-if-not (lambda (x) (and (is-fruit-p x)
                                (eq (color x) (color mine))
                                (>= (weight x) (weight mine))))
               objects)

答案 1 :(得分:4)

quicklisp可安装的alexandria库中提供了disjoinconjoin等高阶函数。

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

答案 2 :(得分: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)

答案 3 :(得分: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)

答案 4 :(得分: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)))