Подтвердить что ты не робот

Как применить "или" к списку в 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))

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

Если вы чувствуете себя лучше, вы в хорошей компании! Это третий вопрос в разделе "Общие ошибки" в Lisp часто задаваемых вопросах:

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

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

Общие функции Lisp КАЖДЫЙ и НЕКОТОРЫЕ могут использоваться для получения функциональность, которую вы намереваетесь при попытке применить # 'AND и #' OR.

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

Ответ 3

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

Ответ 4

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

например.   (конц-листы (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 может быть одной из этих 20-нечетных "функций" в lisp, которые на самом деле не являются функциями, поскольку они не всегда оценивают все параметры.

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

Ответ 6

Ответ Эли Барзилай правильный и идиоматический. Я хочу предоставить альтернативный ответ, основанный на dash.el, в библиотеке, которую я использую, чтобы написать terse функциональный стиль, когда мне приходится работать со списками. or возвращает первый элемент non-nil, иначе в противном случае из-за короткого замыкания. Поэтому просто используйте -first:

(-first 'identity '(nil 0 1 nil)) ; 0
(-first 'identity '(nil nil)) ; nil
Функция

identity просто возвращает свой аргумент. Это умно, потому что -first применяет предикат до тех пор, пока не вернет non-nil. identity возвращает non-nil, если аргумент сам не равен нулю. Если вы просто хотите проверить, нет ли элементов в списке, используйте -any?:

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