2011-05-05 8 views
21

En elisp puedo evaluar o como una función como +.¿Cómo aplico "o" a una lista en elisp

(or nil 0 nil) ==> 0 

(+ 1 0 1) ==> 2 

puedo usar en Aplicar para aplicar + a una lista

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

Por lo tanto, yo creo o funcionaría de la misma manera, pero no es así.

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

Imagino que esto proviene de alguna magia interna utilizada para implementar la evaluación de cortocircuito. ¿Cómo puedo usar apply para ejecutar la operación o sobre una lista?


P.S. mi aplicación deseada es averiguar si alguno de los elementos de la línea de comandos se ajustan a un patrón particular, por lo que la parte importante de lo que estoy escribiendo es:

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

Pero no funciona

Respuesta

20

El problema es que or es una macro (que es la "magia interna" en cuestión), y tienes razón en que eso se hace para que pueda causar un cortocircuito. Si or fuera una función, entonces la llamada debería seguir las reglas habituales para evaluar una llamada a función: se deberían evaluar todos los argumentos antes de realizar la llamada.

Véase también this question - se trata de Scheme pero es exactamente el mismo problema.

En cuanto a una solución, que es mejor usar some, como en:

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

Nota: este utiliza Common Lisp que no está incluido en Emacs por defecto. Solo use (require 'cl)

+1

+1 por ofrecer realmente una solución, algo que olvidé por completo. –

+0

La solución con algunos no se ejecuta. Me sale el error (la función void es algo), lo que quiero decir que algunos no están definidos. – Eponymous

+0

Eponymous: 'some' se define en" cl-extra.el "- es posible que tengas que cargarlo primero. O tal vez estés usando una versión anterior de Emacs. – Ken

0

Solo estoy adivinando aquí, pero creo que or podría ser una de estas 20 'funciones' en lisp que no son realmente funciones, ya que no siempre evalúan todos los parámetros.

Esto tiene sentido para hacer or uno de estos, ya que si usted ha encontrado uno TRUE, se puede detener la búsqueda. En otras palabras, or no es una función como una forma de optimización. Sin embargo, solo estoy adivinando.

+0

Hay * manera * más macros que 20 ... –

+0

Estaba pensando en operadores especiales, que no son ni macros ni funciones. Escuché que hay 25 de ellos (al menos en clisp). No sé mucho más sobre ellos que eso, pero estoy bastante seguro de haber cometido un error al decir que "o" es uno de ellos. –

+0

Bueno, estrictamente hablando, existe una diferencia entre las formas especiales y las macros: las primeras son parte del lenguaje y las últimas no. En el caso de ELisp, 'o' pasa a definirse como un componente interno primitivo, pero eso es solo un problema de implementación ya que podría haberse definido como una macro usando' if'. (Y ELisp no intenta llegar a algo de minimalismo, o hubiera hecho exactamente eso.) –

10

Si te hace sentir mejor, ¡estás en buena compañía! Esta es la tercera cuestión planteada en the "Common Pitfalls" section of the Lisp FAQ:

Ésta es la simple, pero no necesariamente satisfactoria, la respuesta: AND y OR son macros, no funciones; APLICAR y FUNCIONAR solo se puede utilizar para invocar funciones , no macros y operadores especiales.

... y Eli es, por supuesto, justo en el dinero con su sugerencia de utilizar SOME:

Las funciones de Common Lisp CADA y algunos se pueden usar para obtener la funcionalidad tiene la intención cuando se trata de para aplicar # 'AND y #' O.

(La FAQ y esta respuesta son en su mayoría sobre Common Lisp, pero en este caso si se omite el signo # la respuesta es la misma.)

0

Eli Barzilay's answer es correcto e idiomático. Deseo proporcionar una respuesta alternativa basada en dash.el, la biblioteca que uso para escribir el código functional-style, cuando tengo que trabajar con listas. or devuelve el primer elemento no nulo, de lo contrario, debido a un cortocircuito. Por lo tanto, basta con utilizar -first:

función
(-first 'identity '(nil 0 1 nil)) ; 0 
(-first 'identity '(nil nil)) ; nil 

identity simplemente devuelve su argumento. Lo cual es inteligente, porque -first aplica un predicado hasta que devuelve no-nil. identity devuelve no-nil si el argumento es en sí mismo no-nil. Si simplemente desea probar si hay elementos no nulos en una lista, utilice -any? lugar:

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

Si no le importa el rendimiento, utilice (eval (cons 'or '(nil 0 nil)))

+0

Esta respuesta merece mucho más votos positivos, pero creo que las personas han sido entrenadas para tener [miedo a eval] (https: // stackoverflow .com/questions/2571401/why-exactly-is-eval-evil). – Thomas

1

Cuando yo estaba tratando de 'aplicar' una macro a una lista de argumentos, recibí un error de que la función está desatada, lo que significa que, 'aplicar' solo recibe una función, en lugar de una macro, como primer argumento.

Con el fin de solucionar este problema, me escribió una nueva función 'Aplicar-macro' de la siguiente manera:

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

Por ejemplo, escribí una macro para concatenar múltiples listas juntas:

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

p.ej (Conc listas '(ab)' (CD) '(ef)) ;; => (ABCDEF)

Ahora trata de 'apply-macro':

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

funciona y devuelve los mismos salida.

De hecho, se plasmen en:

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

También se puede pasar un formulario a una macro:

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

volver a su pregunta, se resuelve:

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