Отображение функции над двумя списками в elisp

В общем lisp я могу сделать это:

(mapcar #'cons '(1 2 3) '(a b c))

=> ((1 . A) (2 . B) (3 . C))

Как мне сделать то же самое в elisp? Когда я пытаюсь, я получаю сообщение об ошибке:

(wrong-number-of-arguments mapcar 3)

Если elisp mapcar может работать только в одном списке за раз, что такое идомальный способ объединения двух списков в alist?

Ответ 1

Вы хотите mapcar*, который принимает одну или несколько последовательностей (не только списки, как в Common Lisp), а для одного аргумента последовательности работает так же, как и обычный mapcar.

(mapcar* #'cons '(1 2 3) '(a b c))
((1 . A) (2 . B) (3 . C))

И даже если это не определено, вы можете легко свернуть свое:

(defun mapcar* (f &rest xs)
  "MAPCAR for multiple sequences"
  (if (not (memq nil xs))
    (cons (apply f (mapcar 'car xs))
      (apply 'mapcar* f (mapcar 'cdr xs)))))

Ответ 2

Emacs имеет встроенную общую библиотеку Lisp, которая вводит множество функций и макросов Common Lisp, но с cl- префикс. Нет причин избегать этой библиотеки. cl-mapcar - это то, что вы хотите:

(cl-mapcar '+ '(1 2 3) '(10 20 30)) ; (11 22 33)

С dash библиотека обработки списков (см. инструкции по установке), вы можете использовать -zip-with (помните: -zip-with совпадает с cl-mapcar применяется к 2 спискам):

(-zip-with '+ '(1 2 3) '(10 20 30)) ; (11 22 33)

Я не знаю элегантный способ реализовать эквивалент -zip-with для 3 аргументов. Но вы можете использовать -partial из пакета dash-functional, который поставляется с dash (функции от dash-functional требуют Emacs 24). -partial частично применяет эту функцию, поэтому эти 2 вызова функций ниже эквивалентны:

(-zip-with '+ '(1 2) '(10 20)) ; (11 22)
(funcall (-partial '-zip-with '+) '(1 2) '(10 20)) ; (11 22)

Затем вы можете использовать его с помощью функции -reduce:

(-reduce (-partial '-zip-with '+) '((1 2 3) (10 20 30) (100 200 300))) 
; (111 222 333)

Вы можете привязать его к функции с ключевым словом &rest, поэтому эта функция будет принимать различное количество аргументов вместо списка:

(defun -map* (&rest lists)
  (-reduce (-partial 'zip-with '+) lists))