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)
;; やっぱり使い道がわからない・・・