[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.14 srfi-42 - 先行評価的内包表記

Module: srfi-42

このモジュールはジェネリックな内包表記(comprehension)機構を提供します。 この機構は他の言語(Haskell、Pythonなど)では組み込みの機構になっていま す。この機構は豊富な操作手続を提供しているので、リストジェネレータとい うだけではなく、ジェネリックなループ構文(Common Lisp の loop マ クロ並みに強力/邪悪だという人もいます)を提供しています。

この機構は先行評価的に走ります。すなわち、リストを生成する場合、評価時 にすべてのリストを生成します。要素を要求駆動的に生成するわけで はありません。それゆえ、無限列を表現することはできません。それが自然に できる Haskell とは違います。Schemeにおいては無限列を扱うのなら遅延評 価をするように構築されたストリームが使えます (util.stream - ストリームライブラリ参照)。

先行評価的内包表記の例

いくつかの例からはじめましょう。

5番目までの整数の自乗のリストを生成しましょう。

 
(list-ec (: i 5) (* i i)) ⇒ (0 1 4 9 16)

list-ecはリストを生成する内包表記マクロです。 最初のフォーム(: i 5)qualifierと呼ばれ、 繰り返しを行う値の集合を指定します (この例では0以上5未満の整数)。 最後のフォーム(* i i)bodyと呼ばれ、 qualifierが指定する値それぞれにつき評価される通常のScheme式です。

内包表記は複数のqualifierを持つことができます。 次の例は数の対(x y)の集合を生成します。ここでxは 2以上 5未満、yは1以上 x 未満です。

 
(list-ec (: x 2 5) (: y 1 x) (list x y))
  ⇒ ((2 1) (3 1) (3 2) (4 1) (4 2) (4 3))

複数のqualifierはネストするように動作します。つまり、(: x 2 5)は 残りの節—(: y 1 x) および (list x y) を繰り返すように 指定しているということです。

上の2つの例はHaskellで書くと以下のようになります。

 
[ i*i   | i <- [0..4] ]
[ (x,y) | x <- [2..4], y <- [1..x-1] ]

違いに注意:(1) Haskellでは要素になる本体部が先にきて、そのあとに修飾 部(セレクタ)がきます。SRFI-42では本体部は最後になります。(2) SRFI-42で は範囲指定の下限はそれを含み、上限はそれを含みません。

a^3+b^3 = c^3+d^3を満すような数字の集合(a b c d)を列挙し ましょう。

 
(define (taxi-number n)
  (list-ec (: a 1 n)
           (: b (+ a 1) n)
           (: c (+ a 1) b)
           (: d (+ c 1) b)
           (if (= (+ (expt a 3) (expt b 3))
                  (+ (expt c 3) (expt d 3))))
           (list a b c d)))

複数の変数を(ネストするのではなく)同時に変化させたい場合は、 複数のqualifierを次のようにまとめることができます。

 
(list-ec (:parallel (: x '(a b c d)) (: y '(1 2 3 4)))
         (list x y))
  ⇒ ((a 1) (b 2) (c 3) (d 4))

リストだけではなく、他のシーケンスも生成できます。

 
(vector-ec (: i 5) i) ⇒ #(0 1 2 3 4)
(string-ec (: i 5) (integer->char (+ i 65))) ⇒ "ABCDE"

畳み込み演算も適用できます。

 
(sum-ec (: i 1 100) i)
  ⇒ 4950    ;; 1以上100未満の整数の和
(product-ec (: i 1 10) i)
  ⇒ 362880 ;;  1以上10未満の整数の積

内包表記マクロ

それぞれの内包表記は以下のような形式になります。

 
(comprehension-macro qualifierbody)

qualifier …の指定に従ってbodyをくりかえし評価します。内包表記の種類 によって、bodyの結果は(リスト、ベクタ、文字列などに)集約されるか、 (sum、product、min、maxなどによって)畳み込まれるか、あるいは、単に捨て られます。

それぞれのqualifierは、それ以降のqualifierbody をどのように繰り返すかを指定します。qualifierには、繰り返しに 使う値を生成する生成的qualifierと、条件によって値を繰り返しから 省く制御的qualifierがあります。以下のQualifiersの節を参照してください。

いくつかの内包表記では、追加の値がqualifiersの前か、body の後に置かれます。

Macro: do-ec qualifier … body

[SRFI-42] Repeats body. The results of body is discarded. This is for side-effecting operations.

Macro: list-ec qualifier … body

[SRFI-42] Repeats body and collects the results into a list.

Macro: append-ec qualifier … body

[SRFI-42] Repeats body, which must yield a list. Returns a list which is the concatenation of all lists retured by body.

Macro: string-ec qualifier … body
Macro: string-append-ec qualifier … body

[SRFI-42] Repeats body, which must yield a character (in string-ec) or a string (in string-append-ec). Returns a string that consists of the results of body.

Macro: vector-ec qualifier … body

[SRFI-42] Repeats body and collects the results into a vector.

Macro: vector-of-length-ec k qualifier … body

[SRFI-42] This is like vector-ec, except that the length of the result vector is known to be k. It can be more efficient than vector-ec. Unless the comprehension repeats exactly k times, an error is signaled.

Macro: sum-ec qualifier … body
Macro: product-ec qualifier … body

[SRFI-42] body must yield a numeric value. Returns sum of and product of the results, respectively.

Macro: min-ec qualifier … body
Macro: max-ec qualifier … body

[SRFI-42] body must yield a numeric value. Returns maximum and minimum value of the results, respectively. body must be evaluated at least once, or an error is signalled.

Macro: any?-ec qualifier … test
Macro: every?-ec qualifier … test

[SRFI-42] Evaluates test for each iteration, and returns #t as soon as it yields non-#f (for any-ec?), or returns #f as soon as it yields #f (for every?-ec). Unlink the comprehensions introduced above, these stop evaluating test as soon as the condition meets. If the qualifiers makes no iteration, #f and #t are returned, respectively.

Macro: first-ec default qualifier … body
Macro: last-ec default qualifier … body

[SRFI-42] First initializes the result by the value of the expression default, then start iteration, and returns the value of the first and last evaluation of body, respectively. In fact, first-ec only evaluates body at most once.

These procedures are most useful when used with control qualifiers. For example, the following first-ec returns the first set of distinct integers (x, y, z), where x*x+y*y+z*z becomes a square of another integer w.

 
(first-ec #f (:integers w) (: z 1 w) (: y 1 z) (: x 1 y)
          (if (= (* w w) (+ (* x x) (* y y) (* z z))))
          (list x y z w))

Note that the first qualifier, (:integers w), generates infinite number of integers; if you use list-ec instead of first-ec it won't stop.

Macro: fold-ec seed qualifier … expr proc
Macro: fold3-ec seed qualifier … expr init proc

[SRFI-42] Reduces the values produced by expr.

Suppose expr produces a sequence of values x0, x1, …, xN. Fold-ec calculates the following value:

 
(proc xN (…(proc x1 (proc x0 seed))…))

It's similar to fold, except that proc is evaluated within the scope of qualifier … so you can refer to the variables introduced by them. On the other hand, seed is outside of the scope of qualifiers.

Fold-ec3 is almost the same but the initial value calculation. In fold-ec3, seed is only used when qualifiers makes no iteration. Otherwise it calculates the following value:

 
(proc xN (…(proc x1 (init x0))…))

Qualifiers

Generational qualifiers

This type of qualifiers generates (possibly infinite) values over which the rest of clauses iterate.

In the following descriptions, vars refers to either a single identifier, or a series of identifier and a form (index identifier2). The single identifier in the former case and the first identifier in the latter case name the variable to which each generated value is bound. The identifier2 in the latter case names a variable to which a series of integers, increasing with each generated element, is bound. See the following example:

 
(list-ec (: x '(a b c)) x)
  ⇒ (a b c)
(list-ec (: x (index y) '(a b c)) (cons x y))
  ⇒ ((a . 0) (b . 1) (c . 2))
EC Qualifier: : vars arg1 args …

A generic dispatcher of generational qualifiers.

EC Qualifier: :list vars arg1 args …
EC Qualifier: :vector vars arg1 args …
EC Qualifier: :string vars arg1 args …
EC Qualifier: :integers vars
EC Qualifier: :range vars stop
EC Qualifier: :range vars start stop
EC Qualifier: :range vars start stop step
EC Qualifier: :real-range vars stop
EC Qualifier: :real-range vars start stop
EC Qualifier: :real-range vars start stop step
EC Qualifier: :char-range vars min max
EC Qualifier: :port vars port
EC Qualifier: :port vars port read-proc
EC Qualifier: :dispatched vars dispatch arg1 args …
EC Qualifier: :do (lb …) ne1? (ls …)
EC Qualifier: :do (let (ob …) oc …) (lb …) ne1? (let (ib …) ic …) ne2? (ls …)
EC Qualifier: :let vars expr
EC Qualifier: :parallel generator …
EC Qualifier: :while generator expr
EC Qualifier: :until generator expr

Control qualifiers

EC Qualifier: if test
EC Qualifier: not test
EC Qualifier: and test …
EC Qualifier: or test …
EC Qualifier: begin command … expr
EC Qualifier: nested qualifier …

[ < ] [ > ]   [ << ] [ Up ] [ >> ]

This document was generated by Shiro Kawai on November, 22 2009 using texi2html 1.78.