Отображение функции над двумя списками в 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))