Ответ @Rainer Joswig правильный, используйте map-into
. В ссылке приведен пример реализации с использованием макроса loop
. Если вы хотите реализовать map-into
с нуля или используете Emacs Lisp, вы также можете сделать это с помощью dotimes
. В Emacs Lisp dotimes
реализован в subr.el
и не требует CL пакет. Это map-into
с 1 последовательностью для отображения в результирующую последовательность:
(defun map-into (r f xs)
(dotimes (i (min (length r) (length xs)) r)
(setf (elt r i)
(funcall f (elt xs i)))))
Для версии с переменным количеством последовательностей мы должны посыпать наш код apply
и mapcar
:
(defun map-into (r f &rest xss)
(dotimes (i (apply 'min (length r) (mapcar 'length xss)) r)
(setf (elt r i)
(apply f (mapcar (lambda (s) (elt s i))
xss)))))
Однако мы видим, что elt
внутри dotimes
заставляет наш алгоритм работать за O(n2). Мы можем оптимизировать его для работы в O(n), используя mapl
. (спасибо @Joshua Taylor).
(defun map-into (rs f xs)
(mapl (lambda (r x) (setf (car r) (funcall f (car x)))) rs xs))
(defun map-into (rs f &rest xss)
(mapl (lambda (r xs)
(setf (car r)
(apply f (car xs))))
rs
(apply 'mapcar 'list xss))) ;; transpose a list of lists
Причина, по которой setf
не работает внутри mapcar
, заключается в том, что setf
является сложный макрос, который преобразуется в выражение, которое может манипулировать изменяемыми данными. В лямбда-области внутри mapcar
он имеет доступ только к переменной, локальной для этой лямбды, а не к последовательности, переданной самому mapcar
, так как же он должен знать, куда вернуть измененное значение? Вот почему код mapcar
в вопросе возвращает измененный список списков, но не изменяет его на месте. Просто попробуйте (macroexpand '(setf (elt xs 0) (funcall 'cdr (elt xs 0))))
и убедитесь сами.
person
Mirzhan Irkegulov
schedule
27.12.2014