在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))
但它不起作用
答案 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