これまでmap系関数を再帰関数のパターンで実装しました。
- mapcarを実装してみた - Qiita
- maplistを実装してみた - Qiita
- mapcを実装してみた - Qiita
- maplを実装してみた - Qiita
- mapcanを実装してみた - Qiita
- mapconを実装してみた - Qiita
今回は再帰関数のパターンではなく、funcall/applyを使うパターンで実装してみました。
固定長引数のmapcarをつくってみる
(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をつくってみる
(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)
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))
(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です。
整える
(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)
できあがりました。見た目はスッキリしています。末尾再帰ではないのでスタック消費が心配ではあります。
末尾再帰版と比較する
(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)))
追っかけやすいのは末尾再帰版かもしれません。好みの問題ですかね。
最後までご覧頂きありがとうございました。