maplを実装してみた

- [mapcarを実装してみた - Qiita](https://qiita.com/inutomo0123/items/985bd62c8949206030f4)
- [maplistを実装してみた - Qiita](https://qiita.com/inutomo0123/items/6563bdd140ac13f09ec7)
- [mapcを実装してみた - Qiita](https://qiita.com/inutomo0123/items/aaaa815c3c3add5ad615)

今回はmaplを実装してみます。

## maplの挙動を確認してみる

```common-lisp
(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...](http://www.lispworks.com/documentation/lw50/CLHS/Body/f_mapc_.htm)

> 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のリストを取り出してみます。

```common-lisp
(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のリストのリストを取り出してみる

```common-lisp
(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のリストのリストが取り出されました。

## 関数を適用してみる

```common-lisp
(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にする

```common-lisp
(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を除外し、高階関数の適用結果を蓄積しないよう変更します。また、最終的な戻り値をリストの先頭のリストに変更します。

## 整える

```common-lisp
(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を実装します。

最後までご覧いただきありがとうございました。