@Rainer Joswig的答案是正確的,使用map-into
。該鏈接給出了使用loop
宏的示例實現。如果你想從頭開始實現map-into
,或者你使用Emacs Lisp,你也可以使用dotimes
來完成。 Emacs Lisp dotimes
在subr.el
中實現,不需要CL package。這是map-into
1個序列映射到結果序列:
對於帶有序列的變化量,我們必須灑我們的代碼與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(n )中工作。我們可以通過使用mapl
(感謝@Joshua Taylor)來優化它在O(n)中的工作。
(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
是complex macro一個擴展到表達,可以操縱它變異的數據。在mapcar
之內的lambda範圍內,它只能訪問變量,局部於此lambda,而不訪問傳遞給mapcar
本身的序列,所以它應該如何知道將修改後的值放回到哪裏?這就是爲什麼mapcar
問題中的代碼返回列表的修改列表,但不會在原地進行變異。試試(macroexpand '(setf (elt xs 0) (funcall 'cdr (elt xs 0))))
並親自體驗。
使用'elt'來訪問列表中的元素是非常昂貴的。它使得函數的時間代替了n。跟蹤列表的尾巴會更好,這樣你總能得到第一個元素。您可以使用[** mapl **](http://www.lispworks.com/documentation/HyperSpec/Body/f_mapc_.htm)高效地完成此操作。例如,要從每個列表中刪除第一個元素,你可以**(mapl(lambda(tail)(pop(first tail)))list)**。 –
請參閱編輯。新功能在O(n)中工作嗎?另外,是否有可能將變量參數映射簡化爲? –
新功能是O(n),這很好。不過,我認爲你可以簡化** map-into的第二個實現。例如,請參閱http://pastebin.com/W8tLkVBR。 –