今回はmaplを実装してみます。
maplの挙動を確認してみる
(defvar *list1* '(1 11 111))
(defvar *list2* '(2 22 222))
(defvar *list3* '(3 33 333))
(format t "~%~A" (mapl (lambda (&rest lists)
(format t "~%*~A" lists))
*list1* *list2* *list3*))
;;; *((1 11 111) (2 22 222) (3 33 333))
;;; *((11 111) (22 222) (33 333))
;;; *((111) (222) (333))
;;; (1 11 111)
高階関数に渡される引数はCDRのリストです。戻り値はmaplの引数の最初のリストがそのまま返ります。
hyperspecを確認してみる
CLHS: Function MAPC, MAPCAR, MAPCAN, MAPL...
Syntax:
mapc function &rest lists+ => list-1
mapcar function &rest lists+ => result-list
mapcan function &rest lists+ => concatenated-results
mapl function &rest lists+ => list-1
maplist function &rest lists+ => result-list
mapcon function &rest lists+ => concatenated-results
Descriptionを抜粋します。
maplistは、関数がリストの連続したサブリストに適用されること以外は、mapcarと似ています。関数は、まずリスト自体に適用され、次に各リストのcdrに適用され、さらに各リストのcdrのcdrに適用される、というように適用されます。
mapl は、関数を適用した結果が累積されないことを除けば、maplist と似ています。list-1 が返されます。
反復処理は,最も短いリストがなくなると終了し,他のリストの過剰な要素は無視されます。
CDRのリストを取り出してみる
高階関数のlambda引数となるCDRのリストを取り出してみます。
(defun aaa (lists acc)
(cond ((null lists) acc)
(t (aaa (cdr lists)
(append acc `(,(cdr (car lists))))))))
(defun my-mapl-aaa (fn &rest lists)
(aaa lists nil))
(format t "~%~A" (my-mapl-aaa nil *list1* *list2* *list3*))
;;; ((11 111) (22 222) (33 333))
正しく取り出せました。
CDRのリストのリストを取り出してみる
(defun bbb (lists acc)
(cond ((null lists) acc)
(t (bbb (cdr lists)
(append acc `(,(cdr (car lists))))))))
(defun %my-mapl (fn lists acc)
(cond ((member nil lists) acc)
(t (%my-mapl fn
(bbb lists nil)
(append acc `(,lists))))))
(defun my-mapl-bbb (fn &rest lists)
(%my-mapl fn lists nil))
(format t "~%~A" (my-mapl-bbb nil *list1* *list2* *list3*))
;;; (((1 11 111) (2 22 222) (3 33 333))
;;; ((11 111) (22 222) (33 333))
;;; ((111) (222) (333)))
CDRのリストのリストが取り出されました。
関数を適用してみる
(defun ccc (lists acc)
(cond ((null lists) acc)
(t (ccc (cdr lists)
(append acc `(,(cdr (car lists))))))))
(defun %my-mapl (fn lists acc)
(cond ((member nil lists) acc)
(t (apply fn lists)
(%my-mapl fn
(ccc lists nil)
(append acc `(,lists))))))
(defun my-mapl-ccc (fn &rest lists)
(%my-mapl fn lists nil))
(format t "~%~A" (my-mapl-ccc (lambda (&rest lists)
(format t "~%*~A" lists))
*list1* *list2* *list3*))
;;; *((1 11 111) (2 22 222) (3 33 333))
;;; *((11 111) (22 222) (33 333))
;;; *((111) (222) (333))
;;; (((1 11 111) (2 22 222) (3 33 333))
;;; ((11 111) (22 222) (33 333))
;;; ((111) (222) (333)))
正しく高階関数が適用されました。しかし、これはmaplistと同じ挙動、むしろmaplistです。
maplistをmaplにする
(defun ccc (lists acc)
(cond ((null lists) acc)
(t (ccc (cdr lists)
(append acc `(,(cdr (car lists))))))))
(defun %my-mapl (fn lists)
(cond ((member nil lists) t)
(t (apply fn lists)
(%my-mapl fn (ccc lists nil)))))
(defun my-mapl-ccc (fn &rest lists)
(%my-mapl fn lists)
(car lists))
(format t "~%~A" (my-mapl-ccc (lambda (&rest lists)
(format t "~%*~A" lists))
*list1* *list2* *list3*))
;;; *((1 11 111) (2 22 222) (3 33 333))
;;; *((11 111) (22 222) (33 333))
;;; *((111) (222) (333))
;;; (1 11 111)
%my-maplの蓄積変数accを除外し、高階関数の適用結果を蓄積しないよう変更します。また、最終的な戻り値をリストの先頭のリストに変更します。
整える
(defun my-mapl(fn &rest lists)
(labels ((%%my-mapl (lists acc)
(cond ((null lists) acc)
(t (%%my-mapl (cdr lists)
(append acc `(,(cdr (car lists))))))))
(%my-mapl (fn lists)
(cond ((member nil lists) t)
(t (apply fn lists)
(%my-mapl fn (%%my-mapl lists nil)))))))
(%my-mapl fn lists)
(car lists))
(format t "~%~A" (my-mapl (lambda (&rest lists)
(format t "~%*~A" lists))
*list1* *list2* *list3*))
;;; *((1 11 111) (2 22 222) (3 33 333))
;;; *((11 111) (22 222) (33 333))
;;; *((111) (222) (333))
;;; (1 11 111)
できました。次回はmapcanを実装します。
最後までご覧いただきありがとうございました。