mapcarを実装してみた - Qiita
maplistを実装してみた - Qiita
今回はmapcを実装してみます。
mapcの挙動を確認する
(defvar *list1* '(1 11 111))
(defvar *list2* '(2 22 222))
(defvar *list3* '(3 33 333))
(format t "~%~A" (mapc (lambda (&rest lists)
(format t "~%lists:~A" lists))
*list1* *list2* *list3*))
;;; lists:(1 2 3)
;;; lists:(11 22 33)
;;; lists:(111 222 333)
;;; (1 11 111)
mapcの戻り値は引数の最初のリストをそのままでよいようです。高階関数のlambda変数はリストのCARのリストでよいようです。
戻り値を返してみる
(defun my-mapc-aaa (fn &rest lists)
(car lists))
(format t "~%~A" (my-mapc-aaa nil *list1* *list2* *list3*))
;;; (1 11 111)
これで&restの最初のリストがそのまま返ります。
リストのCARのリストを取り出してみる
高階関数の適用対象となるリストを取り出してみます。
(defun bbb (lists acc)
(cond ((null lists) acc)
(t (bbb (cdr lists)
(append acc `(,(car (car lists))))))))
(defun my-mapc-bbb (fn &rest lists)
(bbb lists nil))
(format t "~%~A" (my-mapc-bbb nil *list1* *list2* *list3*))
;;; (1 2 3)
正しく取り出されました。
リストのCDRのリストを取り出してみる。
次の再帰の引数となるリストを取り出してみます。
(defun ccc (lists acc)
(cond ((null lists) acc)
(t (ccc (cdr lists)
(append acc `(,(cdr (car lists))))))))
(defun my-mapc-ccc (fn &rest lists)
(ccc lists nil))
(format t "~%~A" (my-mapc-ccc nil *list1* *list2* *list3*))
;;; ((11 111) (22 222) (33 333))
関数を結合する
(defun ddd (lists acc-car acc-cdr)
(cond ((null lists) `(,acc-car . ,acc-cdr))
(t (ddd (cdr lists)
(append acc-car `(,(car (car lists))))
(append acc-cdr `(,(cdr (car lists))))))))
(defun my-mapc-ddd (fn &rest lists)
(ddd lists nil nil))
(format t "~%~A" (my-mapc-ddd nil *list1* *list2* *list3*))
;;; ((1 2 3) (11 111) (22 222) (33 333))
戻りのリストのCARに関数適用の対象となるリスト。CDRに次の再帰に渡すリストが入っています。
再帰的にリストを取り出してみる
(defun eee (lists acc-car acc-cdr)
(cond ((null lists) `(,acc-car . ,acc-cdr))
(t (eee (cdr lists)
(append acc-car `(,(car (car lists))))
(append acc-cdr `(,(cdr (car lists))))))))
(defun %my-mapc (fn lists acc)
(cond ((member nil lists) acc)
(t (let ((tmp (eee lists nil nil)))
(%my-mapc fn
(cdr tmp)
(append acc `(,(car tmp))))))))
(defun my-mapc-eee (fn &rest lists)
(%my-mapc fn lists nil))
(format t "~%~A" (my-mapc-eee nil *list1* *list2* *list3*))
;;; ((1 2 3) (11 22 33) (111 222 333))
(format t "~%~A" (my-mapc-eee nil '(1) *list2* *list3*))
;;; ((1 2 3))
%my-mapcを再帰させています。my-mapc-eeeの戻り値は、関数適用対象のリストのリストです。
高階関数を適用する
(defun fff (lists acc-car acc-cdr)
(cond ((null lists) `(,acc-car . ,acc-cdr))
(t (fff (cdr lists)
(append acc-car `(,(car (car lists))))
(append acc-cdr `(,(cdr (car lists))))))))
(defun %my-mapc (fn lists acc)
(cond ((member nil lists) acc)
(t (let ((tmp (fff lists nil nil)))
(%my-mapc fn
(cdr tmp)
(append acc `(,(apply fn (car tmp)))))))))
(defun my-mapc-fff (fn &rest lists)
(%my-mapc fn lists nil))
(format t "~%~A" (my-mapc-fff #'+ *list1* *list2* *list3*))
;;; (6 66 666)
(format t "~%~A" (my-mapc-fff #'+ '(1) *list2* *list3*))
;;; (6)
これmapcarと同じです。本来mapcは関数適用の結果を返さないため、%my-mapcの蓄積変数が不要です。また、与えたリストの先頭のリストを返すよう変更します。
mapcarをmapcにする
(defun fff (lists acc-car acc-cdr)
(cond ((null lists) `(,acc-car . ,acc-cdr))
(t (fff (cdr lists)
(append acc-car `(,(car (car lists))))
(append acc-cdr `(,(cdr (car lists))))))))
(defun %my-mapc (fn lists)
(cond ((member nil lists) t)
(t (let ((tmp (fff lists nil nil)))
(apply fn (car tmp))
(%my-mapc fn (cdr tmp))))))
(defun my-mapc-fff (fn &rest lists)
(%my-mapc fn lists)
(car lists))
(format t "~%~A" (my-mapc-fff (lambda (&rest lists)
(format t "~%*~A" lists))
*list1* *list2* *list3*))
;;; *(1 2 3)
;;; *(11 22 33)
;;; *(111 222 333)
;;; (1 11 111)
%my-mapcの蓄積変数accを取り払います。また、最終的に先頭のリストを返すよう修正します。
整理します
(defun my-mapc (fn &rest lists)
(labels ((%%my-mapc (lists acc-car acc-cdr)
(cond ((null lists) `(,acc-car . ,acc-cdr))
(t (%%my-mapc (cdr lists)
(append acc-car
`(,(car (car lists))))
(append acc-cdr
`(,(cdr (car lists))))))))
(%my-mapc (fn lists)
(cond ((member nil lists) t)
(t (let ((tmp (%%my-mapc lists nil nil)))
(apply fn (car tmp))
(%my-mapc fn (cdr tmp)))))))
(%my-mapc fn lists)
(car lists)))
(format t "~%~A" (my-mapc (lambda (&rest lists)
(format t "~%*~A" lists))
*list1* *list2* *list3*))
;;; *(1 2 3)
;;; *(11 22 33)
;;; *(111 222 333)
;;; (1 11 111)
できあがりました。%my-mapcが蓄積変数を必要としないところがmapcarとの違いです。
最後までご覧いただきありがとうございました。