lispとrubyとpythonと その3 リスト(lisp series-mapping)
Lisp、series-mapping編
mappingはseriesの加工。
;; MAP-FN ;; いわゆるmap ;; MAP-FN TYPE FUNCTION &rest SERIES-INPUTS (map-fn 'integer #'(lambda (i) (1+ i)) (subseries (scan 'integer '(1 2 3 4 5)) 0 4)) ;; 出力 ;; #Z(2 3 4 5) ;;複数指定は例によってvaluesで (map-fn '(values integer integer) #'(lambda (a b) (values (1+ a) (1+ b))) (scan 'integer '(1 2 3 4 5)) (scan 'integer '(6 7 8 9 10))) ;; 出力 ;; #Z(2 3 4 5 6) ;; #Z(7 8 9 10 11) ;;SERIES-INPUTSがないとFUNCTIONを呼びつづける (subseries (map-fn 'integer #'(lambda () 'a)) 0 10) ;; 出力 ;; #Z(A A A A A A A A A A) ;;(series::install)していればmap-fnの変わりにマクロ文字 #M が使える ;;名前のある関数なら#Mが使えるよってs-docに書いてあるんだけど、LAMBDAでもいけてる ;;それから#Mだとmap-fnの第2引数のTYPEはT固定になるみたいなんだけど、Tにすると何がどうなるのかよくわからん ;;適当によろしくやってもらうときはTなのかなぁ。 (subseries (#M(lambda () 'a)) 0 10) ;; 出力 ;; #Z(A A A A A A A A A A) (subseries (#M(lambda (i) (+ i 10)) (scan T '(1 2 3 4 5))) 0 3) ;; 出力 ;; #Z(11 12 13) ;; MAPPING ;; 説明しづらい。 ;; MAPPING ({({VAR | ({VAR}*)} VALUE)}*) {DECLARATION}* {FORM}* ;; ようは ;; (MAPPING ((X R) (Y S)) ...) == (MAP-FN T #'(LAMBDA (X Y) ...) R S) ;; ってことなんだけどこれではs-docのままだしなぁ。。。 ;; map-fnを並べ替えてるだけなんだけど。 ;; (map-fn '(values integer integer) #'(lambda (a b) (values (1+ a) (1+ b))) ;; (scan 'integer '(1 2 3 4 5)) ;; (scan 'integer '(6 7 8 9 10))) ;; ↑をmappingで書くとこう↓ ただしTYPEはTになってる (mapping ((a (scan T '(1 2 3 4 5))) (b (scan T '(6 7 8 9 10)))) (values (1+ a) (1+ b))) ;;出力 ;; #Z(2 3 4 5 6) ;; 書いててわかった。 ;; TYPEがTって多値をかえせないってことなんだ。 ;; なるほど ;; 以下は全部同じ。 (map-fn t #'(lambda (a b) (+ a b)) (scan t '(1 2 3 4 5)) (scan t '(6 7 8 9 10))) (#M(lambda (a b) (+ a b)) (scan t '(1 2 3 4 5)) (scan t '(6 7 8 9 10))) (mapping ((a (scan t '(1 2 3 4 5))) (b (scan t '(6 7 8 9 10)))) (+ a b)) ;; 出力 ;; #Z(7 9 11 13 15) ;; 多値をとって分配もできる (mapping (((a b) (values (scan t '(1 2 3 4 5)) (scan t '(6 7 8 9 10))))) (+ a b)) ;; 出力 ;; #Z(7 9 11 13 15) ;; scan-alist、scan-plistと併用するといい感じ (mapping (((a b) (scan-alist '((1 . "aaa") (2 . "bbb") (3 . "ccc"))))) (format nil "~A-~A" a b)) ;; 出力 ;; #Z("1-aaa" "2-bbb" "3-ccc") (mapping (((a b) (scan-plist '(1 "aaa" 2 "bbb" 3 "ccc")))) (format nil "~A-~A" a b)) ;; 出力 ;; #Z("1-aaa" "2-bbb" "3-ccc") ;; ITERATE ;; mappingと動きは同じ ;; ただ、戻り値はnilになる。 ;; mapping が mapcar で iterate は mapc に相当する ;; 副作用だけが目的のときにはこっちを使う ;; ITERATE ({({VAR | ({VAR}*)} VALUE)}*) {DECLARATION}* {FORM}* (mapping (((a b) (scan-plist '(1 "aaa" 2 "bbb" 3 "ccc")))) (print (format nil "~A-~A" a b))) ;; 出力 ;; "1-aaa" ;; "2-bbb" ;; "3-ccc" #Z("1-aaa" "2-bbb" "3-ccc") (iterate (((a b) (scan-plist '(1 "aaa" 2 "bbb" 3 "ccc")))) (print (format nil "~A-~A" a b))) ;; 出力 ;; "1-aaa" ;; "2-bbb" ;; "3-ccc" NIL ;; *切り捨て* ;; cotruncate ;; 短い方に合わせて切り捨て (cotruncate #Z(1 2 3 4 5) #Z(6 7 8)) ;; 出力 ;; #Z(1 2 3) ;; #Z(6 7 8) ;; 片方が無限リストでもこのとおり (cotruncate #Z("a" "b" "c") (scan-range)) ;; 出力 ;; #Z("a" "b" "c") ;; #Z(0 1 2) ;; until ;; 最初の引数がt になったらそこで切り捨て ;; UNTIL BOOLS &rest SERIES-INPUTS (until #Z(nil nil nil nil) #Z(1 2 3) #Z(4 5 6)) ;; 出力 ;; #Z(1 2 3) ;; #Z(4 5 6) ;; 最初の引数の方が数が少ないとそこで切り捨て (until #Z(nil) #Z(1 2 3) #Z(4 5 6)) ;; 出力 ;; #Z(1) ;; #Z(4) ;; tになった時点で切り捨て (until #Z(nil nil nil t nil nil) (scan-range) (scan-range :by 2)) ;; 出力 ;; #Z(0 1 2) ;; #Z(0 2 4) ;;until-if ;;predicateがtになったら切り捨て ;;predicateには引数の一番最初のseriesの要素が順に渡ってくる ;;UNTIL-IF PRED &rest SERIES-INPUTS (until-if #'(lambda (a) (> a 4)) (scan-range) (scan-range :by 2)) ;; 出力 ;; #Z(0 1 2 3 4) ;; #Z(0 2 4 6 8) ;; PREVIOUS ;; PREVIOUS ITEMS &optional (DEFAULT NIL) (AMOUNT 1) ;; AMOUNTに指定した数だけ右シフトする ;; シフトした分DEFAULTで指定した値を詰める ;; これもどう使えば便利なのかピンとこないなあ (previous #z(1 2 3 4)0) ;;出力 ;; #Z(0 1 2 3) ;;LATCH ;;LATCH ITEMS &key :AFTER :BEFORE :PRE :POST ;;先頭から非nilの要素を探して、見つかった要素以降をnilで置き換え。 ;;使いどころがピンとこない (latch #z(nil 1 nil 2 nil 3 nil nil)) ;;出力 ;;#Z(NIL 1 NIL NIL NIL NIL NIL NIL) ;;nil以外で置き換えたい時は:postで指定 (latch #z(nil 1 nil 2 nil 3 nil nil) :post 9) ;;出力 ;;#Z(NIL 1 9 9 9 9 9 9) ;;n個目の非nil要素以降から置き換えを始めたいときは:afterで指定 (latch #z(nil 1 nil 2 nil 3 nil nil) :after 2) ;;出力 ;;#Z(NIL 1 NIL 2 NIL NIL NIL NIL) ;;非nilの要素以前を置き換えるときは:preで指定 ;;n個目の非nil要素以前から置き換えを始めたいときは:beforeで指定 (latch #z(nil 1 nil 2 nil 3 nil nil) :pre 0 :before 3) ;;出力 ;;#Z(0 0 0 0 0 3 NIL NIL) ;;COLLECTING-FN ;;COLLECTING-FN TYPE INIT FUNCTION &rest SERIES-INPUTS [Function] ;;動きはreduceっぽくて、FUNCTIONを呼びだした毎回の結果をseriesにして返す (series::collecting-fn 'intger #'(lambda () 0) #'(lambda (a n) (+ a n)) #z(1 2 3 4)) ;;出力 ;;#Z(1 3 6 10) ;;多値もいける (multiple-value-bind (a b) (series::collecting-fn '(values intger string) #'(lambda () (values 0 "")) #'(lambda (an as n s) (values (+ an n) (format nil "~A_~A:" as s))) #z(1 2 3) #z("aaa" "bbb" "ccc")) (print a) (print b)) ;;出力 ;;#Z(1 3 6) ;;#Z("_aaa:" "_aaa:_bbb:" "_aaa:_bbb:_ccc:") ;;CHOOSE ;;CHOOSE BOOLS &optional (ITEMS BOOLS) ;;BOOLSに渡したseriesでTになってる箇所をoITEMSから抜き出す (choose #z(t nil t nil t) #z(1 2 3 4 5)) ;;出力 ;;#Z(1 3 5) ;;CHOOSE-IF ;;CHOOSE-IF PRED ITEMS ;;PREDに指定した述語に一致するものだけ抜き出す (choose-if #'(lambda (n) (< 3 n)) #z(1 2 3 4 5)) ;;出力 ;;#Z(4 5) ;; EXPAND BOOLS ITEMS &optional (DEFAULT NIL) ;;BOOLSに指定したseriesの非nil部分にITEMSで指定したseriesを順に展開 ;;tの数が足りなければその後のt以降は切り捨て (expand #z(t nil t nil nil t) #z(1 2)) ;;出力 ;;#Z(1 NIL 2 NIL NIL) ;;defaultを指定するとnilの部分は指定した値になる (expand #z(t nil t nil nil t) #z(1 2) 9) ;;出力 ;;#Z(1 9 2 9 9) ;; SPLIT ITEMS &rest TEST-SERIES-INPUTS [Function] ;;TEST-SERIES-INPUTに指定したseriesの非nil、nil箇所でitemsを分ける (split #z(1 2 3 4 5) #z(t nil t nil t)) ;;出力 ;;#Z(1 3 5) ;;#Z(2 4) ;; SPLIT-IF ITEMS &rest TEST-PREDICATES [Function] ;;述語に一致するかしないかでitemsを分ける (split-if #z(1 2 3 4 5) #'(lambda (n) (< 3 n))) ;;出力 ;;#Z(4 5) ;;#Z(1 2 3) ;; CATENATE &rest SERIES-INPUTS [Function] ;;seriesを連結 (catenate #z(1 2 3) #z(a b c) #z(-1 -2 -3)) ;;出力 ;;#Z(1 2 3 A B C -1 -2 -3) ;; SUBSERIES ITEMS START &optional BELOW [Function] ;;一部を取り出す (subseries (scan-fn t #'(lambda () 0) (lambda (n) (1+ n))) 2 5) ;;出力 ;;#Z(2 3 4) ;; POSITIONS BOOLS [Function] ;; 非nilの要素の位置を返す (positions #z(a b nil d nil)) ;;出力 ;;#Z(0 1 3) ;; MASK MONOTONIC-INDICES [Function] ;; tとnilのseriesを作る ;; MONOTONIC-INDICESに指定したインデックスの要素はtに、その他はnilのseriesが返る ;; 無限リストが返るので注意 (subseries (mask #z(0 1 3)) 0 10) ;; 出力 ;; #Z(T T NIL T NIL NIL NIL NIL NIL NIL) ;; MINGLE ITEMS1 ITEMS2 COMPARATOR [Function] ;; 二つのseriesを混ぜる ;; これも動きがややこしい。 ;; 出力を見ながら動きを考えた方が無難 (mingle #z(3 5 7) #z(1 4 8 10) #'(lambda (a b) (format t "a:~A b:~A~%" a b) (< a b))) ;; 出力 ;; a:1 b:3 ;; a:4 b:3 ;; a:4 b:5 ;; a:8 b:5 ;; a:8 b:7 ;; #Z(1 3 4 5 7 8 10) ;;ITEMS1 ITEMS2の最初の要素から順にCOMPARATORに渡されるて比較される ;;まずlambda(a b) の aの方にITEMS2の要素が渡されるので注意 ;;上の例だとITEMS2の第一要素の1とITEMS1の第一要素の3がCOMPARATORに渡される ;;で(< 1 3)になるからtが返る ;;tなのでITEMS2の第一要素「1」が消費される ;;で、ITEMS2の第二要素の4と残ってるITEMS1の第一要素の3がCOMPARATOR。。。と続く ;; CHUNK M N ITEMS [Function] ;; 使いどころがこれもわからん ;; うごきもなんだかよくわからん ;; ITEMSで指定したseriesをM個に分解する ;;(chunk 1 1 #z(1 2 3 4 5 6 7 8 9 10)) ;;なら ;;#Z(1 2 3 4 5 6 7 8 9 10) ;;(chunk 2 1 #z(1 2 3 4 5 6 7 8 9 10)) ;;なら ;;#Z(1 2 3 4 5 6 7 8 9) ;;#Z(2 3 4 5 6 7 8 9 10) ;; (chunk 3 1 #z(1 2 3 4 5 6 7 8 9 10)) ;; なら ;; #Z(1 2 3 4 5 6 7 8) ;; #Z(2 3 4 5 6 7 8 9) ;; #Z(3 4 5 6 7 8 9 10) ;;でN個ごとに要素をピックアップする ;;今までの例だと1だからひとつづつ順番に要素をとってた ;;これのNを2に返るとこんな感じ ;; (chunk 1 2 #z(1 2 3 4 5 6 7 8 9 10)) ;; #Z(1 3 5 7 9) ;; (chunk 2 2 #z(1 2 3 4 5 6 7 8 9 10)) ;; #Z(1 3 5 7 9) ;; #Z(2 4 6 8 10) ;; (chunk 3 2 #z(1 2 3 4 5 6 7 8 9 10)) ;; #Z(1 3 5 7) ;; #Z(2 4 6 8) ;; #Z(3 5 7 9) ;; やっぱり使い道がわからない・・・