【问题标题】:How do I apply "or" to a list in elisp如何将“或”应用于 elisp 中的列表
【发布时间】:2011-08-19 15:48:48
【问题描述】:

在 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 对列表执行 or 操作?


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

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

但它不起作用

【问题讨论】:

    标签: lisp elisp


    【解决方案1】:

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

    另请参阅this question -- 这是关于 Scheme 的,但它是完全相同的问题。

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

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

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

    【讨论】:

    • +1 实际提供了一个解决方案,我完全忘记了。
    • 有些解决方案不执行。我得到了错误(void-function some)——我认为它的意思是一些没有定义。
    • 同名:some 在“cl-extra.el”中定义——您可能需要先加载它。或者您可能使用的是旧版本的 Emacs。
    • 是的,您需要它,因为默认情况下不包含 CL 内容。只需使用(require 'cl)
    【解决方案2】:

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

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

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

    Common Lisp 函数 EVERY 和 SOME 可用于获取 您在尝试应用 #'AND 和 #'OR 时想要的功能。

    (FAQ 和这个答案主要是关于 Common Lisp,但在这种情况下,如果你省略了 # 字符,答案是一样的。)

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 这个答案值得更多的支持,但我猜人们已经被训练成afraid of eval
      【解决方案4】:

      当我尝试将宏“应用”到参数列表时,我收到一个错误,指出该函数未绑定,这意味着“应用”只接收一个函数,而不是一个宏,作为它的第一个参数。

      为了解决这个问题,我编写了一个新函数“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))
      

      例如 (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
      

      【讨论】:

        【解决方案5】:

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

        or 设为其中之一是有意义的,因为如果您找到了一个 TRUE,则可以停止搜索。换句话说,or 不是一种优化形式的函数。不过仍然只是猜测。

        【讨论】:

        • 我在考虑特殊的运算符,它们既不是宏也不是函数。我听说有 25 个(至少在 clisp 中)。我对它们的了解不多,但我很确定我说or 是其中之一是错误的。
        • 好吧,严格来说,特殊形式和宏是有区别的——前者是语言的一部分,而后者不是。在 ELisp 案例中,or 恰好被定义为原始内置函数,但这只是一个实现问题,因为它也可以使用if 定义为宏。 (而且 ELisp 并没有试图达到某种极简主义,否则它会这样做。)
        【解决方案6】:

        Eli Barzilay's answer 是正确且惯用的。我想提供一个基于dash.el 的替代答案,当我必须使用列表时,我使用该库来编写简洁的functional-style 代码。 or 返回 first 非 nil 元素,否则返回 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
        

        【讨论】:

          猜你喜欢
          • 2018-11-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-06-01
          • 1970-01-01
          • 2018-06-29
          相关资源
          最近更新 更多