mapcを実装してみた

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

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

## mapcの挙動を確認する

```common-lisp
(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のリストでよいようです。

## 戻り値を返してみる

```common-lisp
(defun my-mapc-aaa (fn &rest lists)
  (car lists))

(format t "~%~A" (my-mapc-aaa nil *list1* *list2* *list3*))
;;; (1 11 111)
```

これで&restの最初のリストがそのまま返ります。

## リストのCARのリストを取り出してみる

高階関数の適用対象となるリストを取り出してみます。

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

次の再帰の引数となるリストを取り出してみます。

```common-lisp
(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))
```

## 関数を結合する

```common-lisp
(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に次の再帰に渡すリストが入っています。

##再帰的にリストを取り出してみる

```common-lisp
(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の戻り値は、関数適用対象のリストのリストです。

## 高階関数を適用する

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

```common-lisp
(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を取り払います。また、最終的に先頭のリストを返すよう修正します。

## 整理します

```common-lisp
(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との違いです。

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