lispとrubyとpythonと その3 リスト(lisp series-scanners)

Lisp、series-scanners編
リファレンスを見ながらseriesのメソッド一通りみてみた。
Alterationはよくわかんなかったので飛ばしたけど。
えらく長いのでエントリを分けます。
まずはscannersから。

;;seriseを使えるようにする
(require 'series)
(series::install)

;;scannersは非seriesなinputをもらってseriesを返す

;;series関数
;;なんかおかしい??
;;ヘルプには
;;(SERIES 'B 'C) => #Z(B C B C B C ...) 
;;って書いてある
;;無限リストをつくっちゃうので、
;;(subseries (series 'b 'c) 0 5)
;;で
;;#Z(B C B C B)
;;になるのかなと思ったんだけど、SBCLですると
(subseries (series 'b 'c) 0 5)
;;#Z(LIST B C LIST B)
;;こうなっちゃう・・・

;;scan-range
;scan-rangeでの生成
;;http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node350.html#SECTION003421000000000000000
;;scan-range &key (:start 0) (:by 1) (:type 'number):upto :below :downto :above :length
;;:fromが書いてない・・・
;;:startって:fromの間違い?
(format t "~A~%" (scan-range :upto 5))
;;#Z(0 1 2 3 4 5)

(format t "~A~%" (scan-range :below 5))
;;#Z(0 1 2 3 4)

(format t "~A~%" (scan-range :from 10 :downto 5 :by -1))
;;#Z(10 9 8 7 6 5)

(format t "~A~%" (scan-range :from 10 :above 5 :by -1))
;;#Z(10 9 8 7 6)

(format t "~A~%" (scan-range :from .5 :by .1 :upto 1 :type 'float))
;;#Z(0.5 0.6 0.70000005 0.8000001 0.9000001)

;;無限リストなので評価しないように注意
(setf s0 (scan-range))
;;#Z(0 1 2 3...)

;;scan
(format t "~A~%" (scan '(a b c)))
(format t "~A~%" (scan 'string "abcdefg"))

;;scan-sublists
(format t "~A~%" (scan-sublists '(a b c d e)))
;;#Z((A B C D E) (B C D E) (C D E) (D E) (E))

;;scan-multiple 
;;数があってないぶんはnilを補うみたい
(multiple-value-bind (s0 s1)
    (scan-multiple 'list '(a b c d e) '(1 2 3))
  (format t "~A~%" s0)
  (format t "~A~%" s1))
;;#Z(A B C D E)
;;#Z(1 2 3 NIL NIL)

;;scan-lists-of-lists
;;ノードをたぐってseriesに追加
;;scan-lists-of-lists-fringeはノード全体ではなくリーフのみ追加
;;ツリーをフラットにしてくれる感じ
(format t "~A~%" (scan-lists-of-lists '(1 (2 3))))
;;#Z((1 (2 3)) 1 (2 3) 2 3)
(format t "~A~%" (scan-lists-of-lists-fringe '(1 (2 3))))
;;#Z(1 2 3)
;;NIL

;; リストのたぐり方はこんな感じなんだと思う
(defun my-scan-lists-of-lists(lst)
  (if (not lst)
      nil
      (append (list lst) (my-scan-lists-of-lists-a lst))))

(defun my-scan-lists-of-lists-a(lst)
  (if (or (not lst) (atom lst))
      nil
      (let ((hd (car lst))
     (bd (cdr lst)))
 (append 
  (list hd) 
  (my-scan-lists-of-lists-a hd) 
  (my-scan-lists-of-lists-a bd)))))

(defun my-scan-lists-of-lists-fringe(lst)
  (cond ((null lst) nil)
 ((atom lst) (list lst))
 (t
  (let ((hd (car lst))
        (bd (cdr lst)))
    (append 
     (my-scan-lists-of-lists-fringe hd) 
     (my-scan-lists-of-lists-fringe bd))))))

;; CL-USER> (scan-lists-of-lists '((1 2) 3 (4 (5 (6 7) 8) 9) 10))
;; #Z(((1 2) 3 (4 (5 (6 7) 8) 9) 10) (1 2) 1 2 3 (4 (5 (6 7) 8) 9) 4 (5 (6 7) 8) 5 (6 7) 6 7 8 9 10)
;; CL-USER> (my-scan-lists-of-lists '((1 2) 3 (4 (5 (6 7) 8) 9) 10))
;;   (((1 2) 3 (4 (5 (6 7) 8) 9) 10) (1 2) 1 2 3 (4 (5 (6 7) 8) 9) 4 (5 (6 7) 8) 5 (6 7) 6 7 8 9 10)
;; CL-USER> (scan-lists-of-lists-fringe '((1 2) 3 (4 (5 (6 7) 8) 9) 10))
;; #Z(1 2 3 4 5 6 7 8 9 10)
;; CL-USER> (my-scan-lists-of-lists-fringe '((1 2) 3 (4 (5 (6 7) 8) 9) 10))
;;   (1 2 3 4 5 6 7 8 9 10)

;;scan-alist
;;キーとvalueのシリーズに分ける
(multiple-value-bind (key val) (scan-alist '((a . 1) (b . 2) (c . 3) (d . 4)))
    (print key)
    (print val))
;;結果
;;#Z(A B C D) 
;;#Z(1 2 3 4) 

;;(属性 値 属性 値・・・
;;で並んでるのってplistっていうのね
;;alistしかしらなんだ
(multiple-value-bind (key val) (scan-plist '(1 2 3 4 5 6 7))
    (print key)
    (print val))
;;結果
;; #Z(1 3 5 7) 
;; #Z(2 4 6 NIL) 

;;ついで
;;symbol-plist
;;おぼえとくこと

(setf ht (make-hash-table))
(setf (gethash "key1" ht) "data1")
(setf (gethash "key2" ht) "data2")
(setf (gethash "key3" ht) "data3")
(setf (gethash "key4" ht) "data4")

(multiple-value-bind (key val) (scan-hash ht)
    (print key)
    (print val))

;;結果
;; #Z("key4" "key3" "key2" "key1") 
;; #Z("data4" "data3" "data2" "data1") 

;;scan-symbols
;;シンボルをseriesでかえしてくれるみたいなんだけど使いどころが分からん
(scan-symbols :cl-user)

;;結果
;; #Z(RESET UNPROFILE REPORT PROFILE ・・・(略))

;;scan-file
;;ファイルを読み込んで行を要素にしたseriesを返すみたい
;;これは便利かも
(scan-file "./a.txt")
#Z(あああ いいい ううう えええ おおお かかか ききき くくく けけけ こここ)

;;scan-fn
;; 引数は
;; SCAN-FN TYPE INIT STEP &optional TEST
;; TYPEで作るseriesの型を決める
;; valuesで複数指定もできる
;; INITで初期値を作る関数を指定
;; こっちも複数指定するときはvaluesで
;; STEPは次の値を作る関数指定
;; こっちも複数指定するときはvalues
;; TESTは終了条件

;; 単純な例
(subseries
   (scan-fn 'integer
     #'(lambda () 0)
     #'(lambda (i) (1+ i)))
   0 10) ;;無限リストになるからsubseriesで絞る
;; 結果
;; #Z(0 1 2 3 4 5 6 7 8 9)

;; 終了条件を指定してみる
(scan-fn 'integer
  #'(lambda () 0)
  #'(lambda (i) (1+ i))
  #'(lambda (i) (> i 3)))  ;;継続条件じゃなくて終了条件なので注意
;; 結果
;; #Z(0 1 2 3)

;;valuesで複数のseriesを返す
(multiple-value-bind (num str) ;;多値が返るからmultiple-value-bindで受ける
      (scan-fn
       '(values integer string) ;;valusごとquoteするのに注意
       #'(lambda () (values 0 "value+0"))
       #'(lambda (i s)
    (let ((i (1+ i)))
      (values i (format nil "value+~A" i)))))
    (format t "~A~%" (subseries num 0 10))
    (format t "~A~%" (subseries str 0 10)))
    
;; 結果
;; #Z(0 1 2 3 4 5 6 7 8 9)
;; #Z(value+0 value+1 value+2 value+3 value+4 value+5 value+6 value+7 value+8 value+9)

;; ファイルを読んでindexをつけてみた。
(with-open-file (strm "./a.txt")
  (multiple-value-bind (idx line)
      (scan-fn 
       '(values integer string string)
       #'(lambda () 
    (let ((line (read-line strm nil 'eof)))
      (values 0 (format nil "0:~A~%" line) line)))
       #'(lambda (i fl l)
    (let ((line (read-line strm nil 'eof)))
      (values (1+ i)
       (format nil "~A:~A~%" i line)
       line)))
       #'(lambda (i fl l)
    (eql l 'eof)))
    (format t "~A~%" idx)
    (format t "~A~%" line)))

;; 結果
;; #Z(0 1 2 3 4 5 6 7 8 9)
;; #Z(0:あああ
;;  0:いいい
;;  1:ううう
;;  2:えええ
;;  3:おおお
;;  4:かかか
;;  5:ききき
;;  6:くくく
;;  7:けけけ
;;  8:こここ
;; )


;; SCAN-FN-INCLUSIVE
;; よくわかんない
;; INITが返した要素をTESTでテストしない、という以外はscan-fnと同じ??
(scan-fn-inclusive 'integer 
       #'(lambda () 0)
       #'(lambda (i) (1+ i))
       #'(lambda (i) (< i 3)))
;; 結果
;; #Z(0)