如何将“或”应用于elisp中的列表

时间:2011-05-05 19:06:13

标签: lisp elisp

在elisp中,我可以评估或作为函数,就像+。

(or nil 0 nil) ==> 0

(+ 1 0 1) ==> 2

我可以使用apply将+应用到列表

(apply '+ '(1 0 1)) ==> 2

所以,我会想或者会以同样的方式工作,但事实并非如此。

(apply 'or '(nil 0 nil)) ==> error: (invalid-function or)

我想这是来自用于实施短路评估的一些内部魔法。如何使用apply对列表执行或操作?


P.S。我想要的应用程序是找出命令行上的任何元素是否与特定模式匹配,所以我写的重要部分是:

(apply 'or (mapcar (lambda (x) (string-match-p "pattern" x)) command-line-args))

但它不起作用

6 个答案:

答案 0 :(得分:21)

问题在于or是一个宏(这是所讨论的“内部魔法”),你说得对,它可以做短路。如果or是一个函数,那么调用它将需要遵循用于评估函数调用的通常规则:在调用之前需要对所有参数进行求值。

另见this question - 它与Scheme有关,但问题完全相同。

至于解决方案,您应该使用some,如:

(some (lambda (x) (string-match-p "pattern" x)) command-line-args)

注意:这使用默认情况下未包含在emacs中的常见lisp。只需使用(require 'cl)

即可

答案 1 :(得分:10)

如果它让你感觉更好,那你就是一个好伙伴!这是the "Common Pitfalls" section of the Lisp FAQ中的第三个问题:

  

这是简单但不一定令人满意的答案:AND和OR   宏,而不是功能; APPLY和FUNCALL只能用于调用   函数,而不是宏和特殊运算符。

...... Eli当然对他的建议使用SOME是正确的:

  

Common Lisp函数EVERY和SOME可用于获取   尝试应用#'AND和#'时打算使用的功能。

(常见问题和答案主要是关于Common Lisp,但在这种情况下,如果省略#字符答案是相同的。)

答案 2 :(得分:2)

如果您不关心效果,请使用(eval (cons 'or '(nil 0 nil)))

答案 3 :(得分:2)

当我试图“申请”时一个宏到一个参数列表,我得到一个错误,该函数是未绑定的,这意味着,' apply'仅接收函数而不是宏作为其第一个参数。

为了解决这个问题,我写了一个新功能' apply-macro'如下:

(defun apply-macro (macro arg-list)
  (eval
   `(,macro ,@(loop for arg in arg-list
                 collect `(quote ,arg)))))

例如,我编写了一个宏来连接多个列表:

(defmacro conc-lists (&rest lists)
  `(funcall #'concatenate 'list ,@lists))

e.g。     (conc-lists'(a b)'(c d)'(e f));; => (A B C D E F)

现在尝试“应用宏”':

(apply-macro 'conc-lists '((a b) (c d) (e f)))

它可以工作并返回相同的输出。

事实上,它将扩展为:

(eval
   (conc-lists (quote (a b)) (quote (c d)) (quote (e f))))

您还可以将表单传递给宏:

(apply-macro 'conc-lists (maplist #'list '(a b c)))
;;=> ((A B C) (B C) (C))

回到你的问题,它已经解决了:

(apply-macro 'or '(nil 0 nil)) ;;=> 0

答案 4 :(得分:0)

我只是在这里猜测,但我认为or可能是lisp中20多个'函数'之一,它们不是真正的函数,因为它们并不总是评估所有参数。

or作为其中之一是有道理的,因为如果您找到了一个TRUE,则可以停止搜索。换句话说,or不是一种优化形式的功能。仍然只是猜测。

答案 5 :(得分:0)

Eli Barzilay's answer是正确和惯用的。我想提供一个基于dash.el的替代答案,我用来编写简洁functional-style代码的库,当我必须使用列表时。 or返回第一个非零元素,否则由于短路而返回nil。因此,只需使用-first

(-first 'identity '(nil 0 1 nil)) ; 0
(-first 'identity '(nil nil)) ; nil

identity函数只返回其参数。这很聪明,因为-first应用谓词直到它返回非零值。如果参数本身是非零的,则identity返回非零值。如果您只想测试列表中是否存在非零元素,请改用-any?

(-any? 'identity '(nil 0 1 nil)) ; t
(-any? 'identity '(nil nil)) ; nil