mapcarをfuncall/applyで実装してみた

これまでmap系関数を再帰関数のパターンで実装しました。

- [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を実装してみた - Qiita](https://qiita.com/inutomo0123/items/bb5b3f1064cf51c8b07e)
- [mapcanを実装してみた - Qiita](https://qiita.com/inutomo0123/items/fccea1034d71f09835dd)
- [mapconを実装してみた - Qiita](https://qiita.com/inutomo0123/items/073e2e66f5a5098b4a76)

今回は再帰関数のパターンではなく、funcall/applyを使うパターンで実装してみました。

## 固定長引数のmapcarをつくってみる

```common-lisp
(defvar *list1* '(1 11 111))
(defvar *list2* '(2 22 222))
(defvar *list3* '(3 33 333))

(defun my-mapcar (fn arg)
  (cond ((null arg) nil)
        (t (cons (funcall fn (car arg))
                 (my-mapcar fn (cdr arg))))))

(format t "~%~A" (mapcar #'+ *list1*))
;;; (1 11 111)
(format t "~%~A" (my-mapcar #'+ *list1*))
;;; (1 11 111)
```

## 可変長引数のmapcarをつくってみる

```common-lisp
(defun %my-mapcar (fn arg)
  (cond ((null arg) nil)
        (t (cons (funcall fn (car arg))
                 (%my-mapcar fn (cdr arg))))))

(defun my-mapcar (fn &rest args)
  (cond ((member nil args) nil)
        (t (cons (apply fn (%my-mapcar #'car args))
                 (apply #'my-mapcar fn (%my-mapcar #'cdr args))))))

(format t "~%~A" (mapcar #'+ *list1* *list2* *list3*))
;;; (6 66 666)
(format t "~%~A" (my-mapcar #'+ *list1* *list2* *list3*))
;;; (6 66 666)
```

固定長引数のmapcarを可変長引数のmapcarから呼びます。elispのドキュメントを参考にしました。

[Mapping Functions (GNU Emacs Lisp Reference Manual)](https://ayatakesi.github.io/emacs/24.5/elisp_html/Mapping-Functions.html)

## hyperspecのExampleでテストする

>(mapcar #'car '((1 a) (2 b) (3 c))) =>  (1 2 3)
(mapcar #'abs '(3 -4 2 -5 -6)) =>  (3 4 2 5 6)
(mapcar #'cons '(a b c) '(1 2 3)) =>  ((A . 1) (B . 2) (C . 3))

```common-lisp
(format t "~%~A" (my-mapcar #'car '((1 a) (2 b) (3 c))))
;;; (1 2 3)

(format t "~%~A" (my-mapcar #'abs '(3 -4 2 -5 -6)))
;;; (3 4 2 5 6)

(format t "~%~A" (my-mapcar #'cons '(a b c) '(1 2 3)))
;;; ((A . 1) (B . 2) (C . 3))
```

同じ結果が返っているので、OKです。

## 整える

```common-lisp
(defun my-mapcar (fn &rest args)
  (labels ((%my-mapcar (fn arg)
             (cond ((null arg) nil)
                   (t (cons (funcall fn (car arg))
                            (%my-mapcar fn (cdr arg)))))))
    (cond ((member nil args) nil)
          (t (cons (apply fn (%my-mapcar #'car args))
                   (apply #'my-mapcar
                          fn
                          (%my-mapcar #'cdr args)))))))

(format t "~%~A" (mapcar #'+ *list1* *list2* *list3*))
;;; (6 66 666)

(format t "~%~A" (my-mapcar #'+ *list1* *list2* *list3*))
;;; (6 66 666)
```
できあがりました。見た目はスッキリしています。末尾再帰ではないのでスタック消費が心配ではあります。

## 末尾再帰版と比較する

```common-lisp
(defun my-mapcar (fn &rest lists)
  (labels ((%%my-mapcar (lists acc-car acc-cdr)
             (cond ((null (car lists)) `(,acc-car . ,acc-cdr))
                   (t (%%my-mapcar (cdr lists)
                                   (append acc-car `(,(car (car lists))))
                                   (append acc-cdr `(,(cdr (car lists))))))))
           (%my-mapcar (fn lists acc)
             (cond ((member nil lists) acc)
                   (t
                    (let ((tmp (%%my-mapcar lists nil nil)))
                      (%my-mapcar fn
                                  (cdr tmp)
                                  (append acc `(,(apply fn (car tmp))))))))))
    (%my-mapcar fn lists nil)))
```

追っかけやすいのは末尾再帰版かもしれません。好みの問題ですかね。

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