LoginSignup
0
0

More than 1 year has passed since last update.

maplistを実装してみた

Last updated at Posted at 2021-08-15

いつもお世話になるmap系の関数たち。

MAP関数 : セマンティックウェブ・ダイアリー

基本的なmap系関数を実装して、理解してしまおうというのがこの記事の趣旨です。

mapcarを実装してみた - Qiita
に引き続いてmaplistを実装してみます。

maplistの動作を確認する

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

(format t "~%~A" (maplist (lambda (x y z)
                            (format t "~%x:~A y:~A z:~A" x y z))
                          *list1* *list2* *list3*))
;;; x:(1 11 111) y:(2 22 222) z:(3 33 333)
;;; x:(11 111) y:(22 222) z:(33 333)
;;; x:(111) y:(222) z:(333)
;;; (NIL NIL NIL)

リストのCDRをlambda引数として再帰的に高階関数を呼び出す。次の再帰に渡すCDRのリストが生成できれば、それに関数適用するだけなのでmapcarと比べると単純です。

次の再帰に渡すリストを生成する

(defun aaa (list acc)
  (cond ((null (car list)) acc)
        (t (aaa (cdr list)
                (append acc `(,(cdr (car list))))))))

(defun my-maplist-aaa (fn &rest lists)
  (aaa lists nil))

(format t "~%~A" (my-maplist-aaa nil *list1* *list2* *list3*))
 ;;; ((11 111) (22 222) (33 333))

最初の呼び出し時点で、次の呼び出しの引数となるリストが取得できています。

関数適用の対象となるリストを蓄積してみる

(defun bbb (list acc)
  (cond ((null (car list)) acc)
        (t (bbb (cdr list)
                (append acc `(,(cdr (car list))))))))

(defun %my-maplist (fn lists acc)
  (cond ((null (car lists)) acc)
        (t (%my-maplist fn
                        (bbb lists nil)
                        (append acc `(,lists))))))

(defun my-maplist-bbb (fn &rest lists)
  (%my-maplist fn lists nil))

(format t "~%~A" (my-maplist-bbb nil *list1* *list2* *list3*))

;;; (((1 11 111)
;;;   (2 22 222)
;;;   (3 33 333))
;;;  ((11 111)
;;;   (22 222)
;;;   (33 333))
;;;  ((111)
;;;   (222)
;;;   (333)))

%my-maplistが呼び出された際、引数として渡ってきたリストに関数を適用するため、リストをそのまま蓄積しているだけです。

関数を適用してみる

(defun ccc (list acc)
  (cond ((null (car list)) acc)
        (t (ccc (cdr list)
                (append acc `(,(cdr (car list))))))))

(defun %my-maplist (fn lists acc)
  (cond ((null (car lists)) acc)
        (t (%my-maplist fn
                        (ccc lists nil)
                        (append acc `(,(apply fn lists)))))))

(defun my-maplist-ccc (fn &rest lists)
  (%my-maplist fn lists nil))

(format t "~%~A" (my-maplist-ccc (lambda (x y z)
                                   (format t "~%x:~A y:~A z:~A" x y z))
                                 *list1* *list2* *list3*))
;;; x:(1 11 111) y:(2 22 222) z:(3 33 333)
;;; x:(11 111) y:(22 222) z:(33 333)
;;; x:(111) y:(222) z:(333)
;;; (NIL NIL NIL)

hyperspecのsampleを適用してみる

CLHS: Function MAPC, MAPCAR, MAPCAN, MAPL...

(maplist #'append '(1 2 3 4) '(1 2) '(1 2 3))
=> ((1 2 3 4 1 2 1 2 3) (2 3 4 2 2 3))
(maplist #'(lambda (x) (cons 'foo x)) '(a b c d))
=> ((FOO A B C D) (FOO B C D) (FOO C D) (FOO D))
(maplist #'(lambda (x) (if (member (car x) (cdr x)) 0 1)) '(a b a c d b c))
=> (0 0 1 0 1 1 1)
;An entry is 1 if the corresponding element of the input
; list was the last instance of that element in the input list.

(format t "~%~A" (my-maplist-ccc #'append '(1 2 3 4) '(1 2) '(1 2 3)))
;;; ((1 2 3 4 1 2 1 2 3) (2 3 4 2 2 3) (3 4 3) (4))
(format t "~%~A" (my-maplist-ccc #'(lambda (x)
                                     (cons 'foo x))
                                 '(a b c d)))
;;; ((FOO A B C D) (FOO B C D) (FOO C D) (FOO D))
(format t "~%~A" (my-maplist-ccc #'(lambda (x)
                                     (if (member (car x) (cdr x)) 0 1))
                                 '(a b a c d b c)))
;;; (0 0 1 0 1 1 1)

最初のテストの結果が一致しません。それ以外は一致しました。

不具合を修正する

%my-maplistが呼び出された際、listsにNILが見つかった時点で停止してしまえば良いようです。memberでNILを探します。ちなみに、findではNILの検出ができないようです。

(defun ddd (list acc)
  (cond ((null (car list)) acc)
        (t (ddd (cdr list)
                (append acc `(,(cdr (car list))))))))

(defun %my-maplist (fn lists acc)
  (cond ;;;((null (car lists)) acc)
        ((member nil lists) acc)
        (t (%my-maplist fn
                        (ddd lists nil)
                        (append acc `(,(apply fn lists)))))))

(defun my-maplist-ddd (fn &rest lists)
  (%my-maplist fn lists nil))

(format t "~%~A" (my-maplist-ddd (lambda (x y z)
                                   (format t "~%x:~A y:~A z:~A" x y z))
                                 *list1* *list2* '(3 33)))
;;; x:(1 11 111) y:(2 22 222) z:(3 33)
;;; x:(11 111) y:(22 222) z:(33)
;;; (NIL NIL)

(format t "~%~A" (my-maplist-ddd #'append '(1 2 3 4) '(1 2) '(1 2 3)))
;;; ((1 2 3 4 1 2 1 2 3) (2 3 4 2 2 3))

整える

(defun my-maplist (fn &rest lists)
  (labels ((%%my-maplist (list acc)
             (cond ((null (car list)) acc)
                   (t (%%my-maplist (cdr list)
                                    (append acc `(,(cdr (car list))))))))
           (%my-maplist (fn lists acc)
             (cond ((member nil lists) acc)
                   (t (%my-maplist fn
                                   (%%my-maplist lists nil)
                                   (append acc `(,(apply fn lists))))))))
  (%my-maplist fn lists nil)))

(format t "~%~A" (my-maplist (lambda (x y z)
                               (format t "~%x:~A y:~A z:~A" x y z))
                             *list1* *list2* '(3 33)))
;;; x:(1 11 111) y:(2 22 222) z:(3 33)
;;; x:(11 111) y:(22 222) z:(33)
;;; (NIL NIL)

maplistが出来上がりました。次回は、mapcをやりましょうかね。

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