Как мне применить "или" к списку в elisp - PullRequest
21 голосов
/ 05 мая 2011

В 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 для выполнения операции или над списком?


PS Мое желаемое приложение - выяснить, соответствуют ли какие-либо элементы в командной строке определенному шаблону, поэтому важная часть того, что яЯ пишу:

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

Но это не работает

Ответы [ 6 ]

21 голосов
/ 05 мая 2011

Проблема в том, что or - это макрос (который является "внутренней магией", о которой идет речь), и вы правы, что это сделано, чтобы оно могло вызвать короткое замыкание.Если бы or была функцией, то для ее вызова необходимо следовать обычным правилам оценки вызова функции: все аргументы должны быть оценены до вызова.

См. Также thisвопрос - речь идет о Схеме, но это точно такая же проблема.

Что касается решения, вы, вероятно, должны использовать some, например:

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

Примечание: этоиспользует общий lisp, который не включен в emacs по умолчанию.Просто используйте (require 'cl)

10 голосов
/ 06 мая 2011

Если вам от этого станет легче, вы в хорошей компании!Это третий вопрос в разделе «Типичные ошибки» FAQ по Lisp :

Вот простой, но не обязательно удовлетворительный ответ: AND и OR - это макросы, а нефункции;APPLY и FUNCALL могут использоваться только для вызова функций, а не макросов и специальных операторов.

... и Илай, конечно, прав, предлагая использовать SOME:

Функции Common Lisp КАЖДОЕ и НЕКОТОРЫЕ могут использоваться для получения функций, которые вы намереваетесь использовать при попытке # и AND #. *. 1013 *

в основном о Common Lisp, но в этом случае, если вы пропустите символ #, ответ будет тем же.)

2 голосов
/ 22 января 2016

Когда я пытался «применить» макрос к списку аргументов, я получил ошибку о том, что функция не связана, что означает, что «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))

например (conc-lists '(ab)' (cd) '(ef)) ;; => (ABCDEF)

Теперь попробуйте' apply-macro ':

(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
2 голосов
/ 10 апреля 2015

Если вас не волнует производительность, используйте (eval (cons 'or '(nil 0 nil)))

0 голосов
/ 03 августа 2014

Ответ Эли Барзилай правильный и идиоматический. Я хочу предоставить альтернативный ответ, основанный на dash.el, библиотеке, которую я использую для написания краткого кода функционального стиля , когда мне приходится работать со списками. or возвращает первый ненулевой элемент, в противном случае - ноль из-за короткого замыкания. Поэтому просто используйте -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
0 голосов
/ 05 мая 2011

Я только догадываюсь здесь, но я думаю, что or может быть одной из этих 20 с лишним 'функций' в lisp, которые на самом деле не являются функциями, поскольку они не всегда оценивают все параметры.

Имеет смысл сделать or одним из них, поскольку, если вы нашли одно ИСТИНА, вы можете прекратить поиск. Другими словами, or не является функцией как форма оптимизации. Тем не менее, пока только догадываюсь.

...