0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

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

今回は再帰関数のパターンではなく、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)))

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

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?