SERIESサンプル

更新日:2011年12月25日()

概要

説明書には シーケンス と ストリーム と ループの性質を組合せた物、と記述してあるけど、初めての時は実際に使ってみないと感覚がつかめないかも。

このドキュメントはほぼ SERIES に含まれる、s-doc.txt の抜粋です。

あまり Web に日本語で解説のあるサンプルがないようなので、とりあえずで書いてみる。後半に行くほど説明がいいかげんになっているけどご容赦を。

インストール

Quicklisp を利用します。

;; 検索
(ql:system-apropos "series")
;; インストール
(ql:quickload "series")

利用方法

特徴的な例

いろいろと便利。

(series:collect-sum (series:choose-if #'plusp (series:scan '(1 -2 3 -4))))
;; => 4
(series:collect-sum (series:choose-if #'minusp (series:scan '(1 -2 3 -4))))
;; => -6
(series:subseries (series:scan-range :from 0 :by 2) 0 5)
;; => #Z(0 2 4 6 8)

;; ファイル読み込み
(series:scan-file "data.txt" #'read-line)
;; cl-csv となんて組み合せて CSV の処理とか
(series:map-fn t (lambda (x) (first (cl-csv:read-csv x))) (series:scan-file "data.txt" #'read-line))

普通は以下をスクリプトの先頭に書くと series: とかのpackage名を省略できる。あと #Z とか #M が使えるので便利

(require :series)
(use-package :series)
(series::install)

以下のサンプルでは require だけした時の例で書いている。

function,macroなど

Series Functions

  • series arg &rest args

series を表現するのに #Z(…)の文法が利用されてます。

;; b と c を繰り返す(永久に出力します)
(series:series 'b 'c)
;; => #Z(B C B C B C…)

;; まあこれだと使いずらいので、scan を利用します
;; scan は list を series に変換します
(series:scan (list 'a 'b 'c))
;; => #Z(A B C)

;; 通常以下のように組合せて利用すると思います
(series:choose-if #'symbolp (series:scan (list 'a '2 'b)))

Scanners

Scanners は series ではない入力を series に変換します。

  • scan-range &key (:start 0) (:by 1) (:type 'number) :upto :below :downto :above :length

条件まちがえるとあたりまえですが、永久出力になります。

;; 0 から 4 以下の series を生成
(series:scan-range :upto 4)
;; => #Z(0 1 2 3 4)

;;  1 から -1 ずつ -4 までの series を生成
(series:scan-range :from 1 :by -1 :above -4)
;; => #Z(1 0 -1 -2 -3)
  • scan {type} sequence

sequence を series に変換する。

;; list を series に変換する
(series:scan '(a b c))
;; => #Z(A B C)

;; 文字列を series に変換する
(series:scan 'string "BAR")
;; => #Z(#\B #\A #\R)
  • scan-sublists list

list の内容からサブリストを作成する。

(series:scan-sublists '(a b c))
;; => #Z((A B C) (B C) (C))
  • scan-multiple type first-sequence &rest more-sequences

複数の sequence を scan する。

;; 複数 sequence を scan
(series:scan-multiple 'list '(1 6 3 2 8) '(2 3 3 3 2))
(series:scan-multiple 'string "FOO" "BAR")

;; 二つの list を scan して、乗算
(multiple-value-bind (data weights)
  (series:scan-multiple 'list '(1 6 3 2 8) '(2 3 3 3 2))
    (series:collect (series:map-fn t #'* data weights)))
;; => (2 18 9 6 16)
  • scan-lists-of-lists lists-of-lists &optional leaf-test
  • scan-lists-of-lists-fringe lists-of-lists &optional leaf-test

複数の list から n-ary tree を生成する。

(series:scan-lists-of-lists '((2) (nil)))
;; => #Z(((2) (NIL)) (2) 2 (NIL) NIL)

(series:scan-lists-of-lists-fringe '((2) (nil)))
;; => #Z(2 NIL)

(series:scan-lists-of-lists-fringe '((2) (nil))
                          #'(lambda (e) (numberp (car e))))
;; => #Z((2) NIL)
  • scan-alist alist &optional (test #'eql)
  • scan-plist plist
  • scan-hash table

連想リスト(alist)、プロパティリスト(plist)、ハッシュテーブルを渡すと、key と value でそれぞれ series に変換する。

(series:scan-plist '(a 1 b 3))
;; => #Z(A B), #Z(1 3)
(series:scan-alist '((a . 1) nil (a . 3) (b . 2)))
;; => #Z(A B), #Z(1 2)
  • scan-symbols &optional (package *package*)

symbol を series にします。

;; series の symbol を取得
(series:scan-symbols :series)
  • scan-file file-name &optional (reader #'read)
  • scan-stream stream &optional (reader #'read)

file や stream を series にする。入出力とかの時に便利

;; ファイルを読んで series に変換
(series:scan-file "test.txt" #'read-line)
  • scan-fn type init step &optional test
  • scan-fn-inclusive type init step test

高階関数な scan。

;; 二つずつ null になるまで取りだし
(series:scan-fn t #'(lambda () '(a b c d)) #'cddr #'null)
;; => #Z((A B C D) (C D))

;; フィボナッチ数列
(series:scan-fn '(values integer integer)
       #'(lambda () (values 1 0))
       #'(lambda (i sum) (values (+ i 1) (+ sum i))))
;; => #Z(1 2 3 4 ...), #Z(0 1 3 6 ...)

Mapping

  • map-fn type function &rest series-inputs

高階関数な map。

;; 足し算
(series:map-fn 'integer #'+ (series:scan '(1 2 3)) (series:scan '(4 5)))
;; => #Z(5 7)

;; 分数割り算と余り
(series:map-fn '(values integer rational) #'floor (series:scan '(1/4 9/5 12/3)))
;; => #Z(0 1 4), #Z(1/4 4/5 0)
  • mapping ({({var | ({var}*)} value)}*) {declaration}* {form}*

以下のような感じ。

;; mapping と map-fn
(mapping ((x r) (y s)) ...) == (map-fn t #'(lambda (x y) ...) r s)

;; mapping の例
(series:mapping ((x (series:scan '(2 -2 3))))
              (expt (abs x) 3))
 ;; => #Z(8 8 27)
  • iterate ({({var | ({var}*)} value)}*) {declaration}* {form}*

declaration を iterate する。

(let ((item (series:scan '((1) (-2) (3)))))
(series:iterate ((x (series:map-fn T #'car item)))
  (if (plusp x) (prin1 x))))
;; => NIL (その後に 13 が表示される)

Truncation and Other Simple Transducers

  • cotruncate &rest series-inputs
  • until bools &rest series-inputs
  • until-if pred &rest series-inputs
;; 同じ項目数にする
(series:cotruncate (series:scan '(1 2 -3 4)) (series:scan '(a b c)))
;; => #Z(1 2 -3), #Z(A B C)

;; t になるまで
(series:until (series:scan '(nil nil t nil)) (series:scan '(1 2 -3 4)) (series:scan '(a b c)))
;; => #Z(1 2), #Z(A B)

;; 条件になるまで
(series:until-if #'minusp (series:scan '(1 2 -3 4)) (series:scan '(a b c)))
;; => #Z(1 2), #Z(A B)
  • previous items &optional (default nil) (amount 1)

右にシフトさせる

;; シフトさせて 0 で埋める
(series:previous (series:scan '(10 11 12)) 0)
;; => #Z(0 10 11)
  • latch items &key :after :before :pre :post

ラッチ回路の動作をする。

(series:latch (series:scan '(nil c nil d e)))
;; => #Z(NIL C NIL NIL NIL)

(series:latch (series:scan '(nil c nil d e)) :before 2 :post t)
;; => #Z(NIL C NIL T T)
  • collecting-fn type init function &rest series-inputs

高階関数。

;; 入力した series を加算している。フィボナッチ数列
(series:collecting-fn 'integer #'(lambda () 0) #'+ (series:scan '(1 2 3)))
;; => #Z(1 3 6)

;; 加算と乗算
(series:collecting-fn '(values integer integer)
             #'(lambda () (values 0 1))
             #'(lambda (sum prod x y)
                 (values (+ sum x) (* prod y)))
             (series:scan '(4 6 8))
             (series:scan '(1 2 3)))
;; => #Z(4 10 18), #Z(1 2 6)

Conditional and Other Complex Transducers

  • choose bools &optional (items bools)
  • choose-if pred items

条件を与えて選択する。

;; t になっている物を選択
(series:choose (series:scan '(t nil t nil)) (series:scan '(a b c d)))
;; => #Z(A C)

;; プラスの値だけ選択して加算
(series:collect-sum (series:choose-if #'plusp (series:scan '(-1 2 -3 4))))
;; => 6
  • expand bools items &optional (default nil)

choose の準逆元 (quasi-inverse)。

(series:expand (series:scan '(nil t nil t t)) (series:scan '(a b c)))
;; => #Z(NIL A NIL B C)

(series:expand (series:scan '(nil t nil t t)) (series:scan '(a)))
;; => #Z(NIL A NIL)
  • spread gaps items &optional (default nil)

expand は boolean で条件を与えるが、spread は gaps で条件を与えるのが違う。動作はほぼ同じ。

;; 1 ずつ 間を NIL で埋める
(series:spread (series:scan '(1 1 1)) (series:scan '(a b c)))
;; => #Z(NIL A NIL B NIL C)

;; 0 2 3 で 間を NIL で埋める
(series:spread (series:scan '(0 2 3)) (series:scan '(a b c)))
;; => #Z(A NIL NIL B NIL NIL NIL C)

;; gaps の項目数で終了となる
(series:spread (series:scan '(2)) (series:scan '(a b c)))
;; => #Z(NIL NIL A)
  • split items &rest test-series-inputs
  • split-if items &rest test-predicates

分割する。

;; boolean を与えて分割する
(series:split (series:scan '(-1 2 3 -4)) (series:scan '(t nil nil t)))
;; => #Z(-1 -4), #Z(2 3)

;; 条件を与えて分割。この例では分割した結果をそれぞれ足している
(multiple-value-bind (+x -x) (series:split-if  (series:scan '(-1 2 3 -4)) #'plusp)
   (values (series:collect-sum +x) (series:collect-sum -x)))
;; => 5, -5
  • catenate &rest series-inputs

二つ以上の series を一つに結合する。

(series:catenate (series:scan '(b c)) (series:scan '()) (series:scan '(d)))
;; => #Z(B C D)
  • subseries items start &optional below

引数によって series を抽出。

;; 1 から取り出し
(series:subseries (series:scan '(a b c d)) 1)
;; => #Z(B C D)

;; 1 から 3 まで取り出し
(series:subseries (series:scan '(a b c d)) 1 3)
;; => #Z(B C)
  • positions bools

nil でない項目の index を series として返す

(series:positions (series:scan '(t nil t 44)))
;; => #Z(0 2 3)
  • mask monotonic-indices
(series:mask (series:scan '()))
;; => #Z(NIL NIL ...)

(series:mask (series:scan '(0 2 3)))
;; => #Z(T NIL T T NIL NIL ...)

(series:mask (series:positions (series:scan '(nil a nil b nil))))
;; => #Z(NIL T NIL T NIL ...)
  • mingle items1 items2 comparator

二つの series を結合し、 comparator にしたがってソートする。

(series:mingle (series:scan '(1 3 7 9)) (series:scan '(4 5 8)) #'<)
;; => #z(1 3 4 5 7 8 9)

(series:mingle (series:scan '(1 7 3 9)) (series:scan '(4 5 8)) #'<)
;; => #z(1 4 5 7 3 8 9)
  • chunk m n items

n 個飛しで、m 回 items を分割する。

;; 単純分割のサンプル
(series:chunk 1 1 (series:scan '(1 5 3 4 5 6)))
;; => #Z(1 5 3 4 5 6)
(series:chunk 2 1 (series:scan '(1 5 3 4 5 6)))
;; => #Z(1 5 3 4 5), #Z(5 3 4 5 6)
(series:chunk 3 1 (series:scan '(1 5 3 4 5 6)))
;; => #Z(1 5 3 4), #Z(5 3 4 5), #Z(3 4 5 6)
(series:chunk 1 2 (series:scan '(1 5 3 4 5 6)))
;; => #Z(1 3 5)

;; 分割して、計算
(series:mapping (((xi xi+1 xi+2) (series:chunk 3 1 (series:scan '(1 5 3 4 5 6)))))
(/ (+ xi xi+1 xi+2) 3))
;; => #Z(3 4 4 5)

;; 分割して cons
(series:collect
  (series:mapping (((prop val) (series:chunk 2 2 (series:scan '(a 2 b 5 c 8)))))
    (cons prop val)))
;; => ((A . 2) (B . 5) (C . 8))

Collectors

series から list 等の非 series に変換する。

  • collect-first items &optional (default nil)
  • collect-last items &optional (default nil)
  • collect-nth n items &optional (default nil)

first,last,nth して返す。

(series:collect-first (series:scan '()) 'z)
;; => Z

(series:collect-last (series:scan '(a b c)))
;; => C

(series:collect-nth 1 (series:scan '(a b c)))
;; => B
  • collect-length items

series の要素数を返す。

(series:collect-length (series:scan '(a b c)))
;; => 3
  • collect-sum numbers &optional (type 'number)

入力した series の elements を加算する。空の場合は 0 を返す

(series:collect-sum (series:scan '(1.1 1.2 1.3)))
;; => 3.6

(series:collect-sum (series:scan '()) 'complex)
;; => 0
  • collect-product numbers &optional (type 'number)

series の elements を乗算する。空の場合は 1 を返す。

(series:collect-product (series:scan '(1.1 1.2 1.3)))
;; => 1.716

(series:collect-product (series:scan '()) 'float)
;; => 1.0
  • collect-max numbers &optional (items numbers) (default nil)
  • collect-min numbers &optional (items numbers) (default nil)

最大値、最小値を取得する。

(series:collect-max (series:scan '(2 1 4 3)))
;; => 4

(series:collect-min (series:scan '(1.2 1.1 1.4 1.3)))
;;=> 1.1

(series:collect-min (series:scan '()))
;; => NIL
  • collect-and bools

series の elements を AND 演算した結果が返る。要素が一つでも NIL なら NIL になる。

(series:collect-and (series:scan '(a b c)))
;; => C

(series:collect-and (series:scan '(a nil c)))
;; => NIL
  • collect-or bools

series の elements を OR 演算した結果が返る。最初の NIL でない要素が返る。空の場合は NIL が返る。

(series:collect-or (series:scan '(nil b c)))
;; => B

(series:collect-or (series:scan '()))
;; => NIL
  • collect items
  • collect type items

series を sequence に変換する。標準は list。type で指定できる。

(series:collect (series:scan '(a b c)))
;; => (A B C)

(series:collect '(vector integer 3) (series:scan '(1 2 3)))
;; => #(1 2 3)
  • collect-append sequences
  • collect-append type sequences

series を結合して sequence に変換する。type で型を指定できる。標準は list。

(series:collect-append (series:scan '((a b) nil (c d))))
;; => (A B C D)

(series:collect-append 'string (series:scan '("a " "big " "cat")))
;; => "A BIG CAT"
  • collect-ignore lists

collect と内部動作は同じだが、結果は破棄される。遅延評価なので必要になる。ちょっとした所で便利。

  • collect-nconc lists

結果と同時に series の elements が取れる以外は collect-append と動作は同じ。

  • collect-alist keys values
  • collect-plist keys values
  • collect-hash keys values &key :test :size :rehash-size :rehash-threshold

alist、plist、hash に変換する。

(series:collect-alist (series:scan '(a b c)) (series:scan '(1 2)))
;; => ((B . 2) (A . 1))

(series:collect-hash (series:scan '()) (series:scan '(1 2)) :test #'eq)
;; => <an empty hash table>
  • collect-file file-name items &optional (printer #'print)
  • collect-stream stream items &optional (printer #'print)

ファイルやストリームに series を出力。

  • collect-fn type init function &rest series-inputs

高階関数。

(series:collect-fn 'integer #'(lambda () 0) #'+ (series:scan '(1 2 3)))
;; => 6

(series:collect-fn 'integer #'(lambda () 0) #'+ (series:scan '()))
;; => 0

Alteration of Series

  • alter destinations items

特定の位置の elements を変更する

(let* ((data (list 1 -2 3 4 -5 6))
     (x (series:choose-if #'minusp (series:scan data))))
;; この時点で x は #Z(-2 -5)
(series:alter x (series:map-fn t #'* x x))
data)
;; => (1 4 3 4 25 6)
  • to-alter items alter-fn &rest args
(defun scan-list (list)
(declare (optimizable-series-function))
(let ((sublists (series:scan-sublists list)))
  (series:to-alter (series:map-fn t #'car sublists)
            #'(lambda (new parent) (setf (car parent) new))
            sublists)))

Generators

  • generator series
  • next-in generator {action}*

generator と next-in を利用してループ等する。便利。 generator はジェネレータを読む。説明は割愛。

(let ((x (series:generator (series:scan '(1 2 3 4)))))
(with-output-to-string (s)
  (loop (prin1 (series:next-in x (return)) s)
        (prin1 (series:next-in x (return)) s)
        (princ "," s))))
;; => "12,34,"

Gatherers

  • gatherer collector
  • next-out gatherer item
  • result-of gatherer

ギャザラと読む。溜め込む機構。便利。

(let ((g (series:gatherer #'series:collect-sum)))
(dolist (i '(1 2 3 4))
  (series:next-out g i)
  (if (evenp i) (series:next-out g (* 10 i))))
(series:result-of g))
;; => 70
  • gathering ({(var fn)}*) {form}*
(defun examp (data)
(series:gathering ((x series:collect) (y series:collect-sum))
  (series:iterate ((i (series:scan data)))
    (case (first i)
      (:slot (series:next-out x (second i)))
      (:part (dolist (j (second i)) (series:next-out x j))))
    (series:next-out y (third i)))))

(examp '((:slot a 10) (:part (c d) 40)))
;; => (A C D) and 50

ライセンス表示

(C)Copyright Massachusetts Institute of Technology, Cambridge, Massachusetts.

Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that this copyright and permission notice appear in all copies and supporting documentation, and that the name of M.I.T. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. M.I.T. makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.

Author: sakito Updated: 2011年12月25日() category: /cl Permalink: Permalink
このエントリーをはてなブックマークする はてなブックマーク数 このエントリーをdel.icio.usに登録する このエントリーを Tumblr する このエントリーに Twitter 経由でコメントする
以下はゲストコメント可能です。名前とメールアドレスは任意の物を入力していただいてかまいません。
blog comments powered by Disqus

このページの先頭へ

copyright (CC-by) 1999-2016 sakito
Powered by pyblosxom. Theme: TheBuckmaker.