For Development HEAD DRAFTSearch (procedure/syntax/module):

4.5 条件式

Special Form: if test consequent alternative
Special Form: if test consequent

[R7RS base] まずtestを評価し、それが真の値を返したらconsequentを評価します。 そうでなければalternativeを評価します。もしalternativeが与えられて いなければ未定義の値を返します。

(if (number? 3) 'yes 'no) ⇒ yes
(if (number? #f) 'yes 'no) ⇒ no

(let ((x '(1 . 2)))
  (if (pair? x)
      (values (car x) (cdr x))
      (values #f #f)))
  ⇒ 1 and 2
Special Form: cond clause1 clause2 …

[R7RS+ base][SRFI-61] 各clause節は次のいずれかの形式でなければなりません。

(test expr ...)
(test => expr)
(test guard => expr)
(else expr expr2 ...)

最後の形式は最後の節のみに許されます。

最初の節から順にtestが評価され、それが真の値を返すまで次の節のtestが 評価されます。testが真の値を返したら、それが最初の形式であれば その節のexprが順に評価され、最後の評価値が戻り値となります。 それが2番目の形式であれば、exprがまず評価されます。 exprは引数をひとつ取る手続きを返さねばなりません。 続いて、testの結果がその手続きに渡され、その手続きの戻り値がcond形式の 評価値となります。

3番目の形式はSRFI-61で定義されています。この形式では、testは 任意の数の値に評価されることができます。それらの値がまず guardに渡され、もしguardが真の値を返したら、同じ引数がexprに 適用されて、その戻り値がcond形式の評価値となります。 guard#fを返した場合は次の節へと評価が進みます。 guardexpr は、testが返すのと同数の引数を取れなければいけません。

もし全てのテストが偽の値を返し、最後の節が4番目の形式(else節)でなければ、未定義の値が返されます。

最後の節がelse節で、他の全てのテストが失敗した場合、else節のexpr が順に評価され、その最後の値がcond形式の値となります。

(cond ((> 3 2) 'greater)
      ((< 3 2) 'less)) ⇒ greater
(cond ((> 3 3) 'greater)
      ((< 3 3) 'less)
      (else 'equal)) ⇒ equal
(cond ((assv 'b '((a 1) (b 2))) => cadr)
      (else #f)) ⇒ 2
Special Form: case key-expr clause1 clause2 …

[R7RS+ base][SRFI-87] key-exprは1つの値を生成する任意の式です。 clauseは以下の形式でなければなりません。

((datum ...) expr expr2 ...)
((datum ...) => proc)

ここで、各datumはSchemeオブジェクトの外部表現であり、全てのdatumは 異なっていなければなりません。最後のclauseには次の形式を持つelse節が許されます。

(else expr expr2 ...)
(else => proc)

まずkey-exprが評価され、その結果がそれぞれのdatumと比較されます。 key-exprの値とeqv?(等価参照)を使って一致するdatum が見つかれば、対応するexprが順に評価され、その最後の値がcaseの 値となります。=>を含む節はSRFI-87で定義されています。これらの節では、 key-exprの結果がprocに渡され、その結果がcaseの値となります。

もし一致するdatumが見つからない場合、else節が与えられていれば そのexprが順に評価され、最後の値が返されます。else節がなければcase節 の値は未定義です。

(case (* 2 3)
  ((2 3 5 7) 'prime)
  ((1 4 6 8 9) 'composite)) ⇒ composite

(case (car '(c d))
  ((a) 'a)
  ((b) 'b)) ⇒ undefined

(case (car '(c d))
  ((a e i o u) 'vowel)
  ((w y) 'semivowel)
  (else 'consonant)) ⇒ consonant

(case 6
  ((2 4 6 8) => (cut + <> 1))
  (else => (cut - <> 1))) ⇒ 7

(case 5
  ((2 4 6 8) => (cut + <> 1))
  (else => (cut - <> 1))) ⇒ 4
Macro: ecase key-expr clause1 clause2 …

このフォームはほぼcaseと同等ですが、else節が与えられず、 key-exprの値がclause中のどのdatumとも一致しなかった場合の 動作だけが異なります。caseではそのような場合は未定義値が返されますが、 ecaseはエラーを報告します。

このマクロはCommon Lispから採られました。想定外の値が渡されることを 念のために検出したい、という場合に便利です。

(ecase 5 ((1) 'a) ((2 3) 'b) ((4) 'c))
 ⇒ ERROR: ecase test fell through: got 5, expecting one of (1 2 3 4)
Macro: typecase key-expr clause1 clause2 …

これはCommon Lispから採られました。 key-exprはひとつの値を生成するScheme式です。 各clauseは次のいずれかの形式でなければなりません。 else節は、現れるなら最後のclauseでなければなりません。

(type-expr expr ...)
(else expr1 expr2 ...)

type-exprは型の式でなければなりません (型とクラス参照)。

このマクロはまずkey-exprを評価し、次にその値が型type-exprであるかどうかを 順番に検査してゆきます。もし型の制約が満たされたら、その節にあるexpr … が評価され、その最後の値がtypecaseの値となります。 型の制約はof-type?で検査されます (汎用型述語参照)。 どの節の型でもなかった場合、else節があればその中の式が順に評価されて最後の値が typecaseの値となり、else節がなければ未定義値が返されます。

(typecase key-expr
  (<integer> 'a)
  ((</> <string> <symbol>) 'b))
 ≡
(let ((tmp key-expr))
  (cond
    ((of-type? tmp <integer>) 'a)
    ((of-type? tmp (</> <string> <symbol>)) 'b)))
Macro: etypecase key-expr clause1 clause2 …

typecaseと似ていますが、key-exprの結果が どのtype-exprの型でもなく、またelse節がなかった場合、 エラーが投げられます。else節がある場合は typecaseと同じです。

(etypecase (sqrt -2)
  (<integer> 'int)
  (<real> 'real))
 ⇒ *** ERROR: etypecase fell through: expecting one of types
    in (<integer> <real>), but got 0.0+1.4142135623730951i
Special Form: and test …

[R7RS base] test式が順に評価されます。最初に偽の値を返したところで評価が止まり、 偽の値が返されます。残りの式は評価されません。 もし全ての式が真の値を返した場合は、最後の式の値が返されます。 式が与えれない場合は#tが返されます。

(and (= 2 2) (> 2 1)) ⇒ #t
(and (= 2 2) (< 2 1)) ⇒ #f
(and 1 2 'c '(f g))   ⇒ (f g)
(and)                 ⇒ #t
Special Form: or test …

[R7RS base] test式が順に評価されます。最初に真の値を返したところで評価が止まり、 その値が返されます。残りの式は評価されません。 もし全ての式が偽の値を返した場合は、偽の値が返されます。 式が与えれない場合は#fが返されます。

(or (= 2 2) (> 2 1)) ⇒ #t
(or (= 2 2) (< 2 1)) ⇒ #t
(or #f #f #f)        ⇒ #f
(or (memq 'b '(a b c))
    (/ 3 0)) ⇒ (b c)
Special Form: when test expr1 expr2 …
Special Form: unless test expr1 expr2 …

[R7RS base] まずtestが評価されます。それが真の値(unlessの場合は偽の値)を返した場合、 引続きexpr1およびexpr2 …が順に評価され、最後の評価値が返されます。 そうでなければ、未定義の値が返されます。

Macro: assume test-expr [message obj …]

[SRFI-145] test-exprを評価してその値を返します。

また、このフォームに続くコードパスにおいて、test-exprが常に満たされている、 というプログラマの意図を宣言します。

Gaucheでは今のところ、text-exprが偽となった場合は常にエラーが投げられます。

(define (rsqrt x)
  (assume (and (real? x) (>= x 0)))
  (sqrt x))

gosh> (rsqrt -1)
*** ERROR: Invalid assumption: (and (real? x) (>= x 0))

省略可能な引数 message obj … が与えられた場合、 それらはtest-exprが偽を返した際にerrorの引数に使われます。

(define (rsqrt x)
  (assume (and (real? x) (>= x 0))
          "Argument must be nonnegative real number, but got:" x)
  (sqrt x))

gosh> (rsqrt -1)
*** ERROR: Argument must be nonnegative real number, but got: -1

註: このフォームは勧告として機能します。 test-exprが失敗してもエラーが投げられるとは限りません。例えば、 将来、最適化オプションをつけて、 速度最適化レベルが高い時はテストを省略するようになるかもしれません。 また一方で、この情報を利用してコンパイラがより良いコードを出すように拡張される 可能性もあります。例えば上のreal-sqrtの例では、コンパイラは理論的には (sqrt x)が実関数として動作すれば良いということを演繹できるため、 実数に特化したコードを生成できる可能性があります。 このフォームを、コードの書き手の意図をコンパイラやコードの読み手に知らせるために 用いると良いでしょう。

Macro: assume-type expr type [message obj …]

exprを評価し、その値が型typeかどうかチェックします。 型が合わなければエラーを投げます。exprの値が返されます。

typeにはGaucheのクラスか記述的な型を指定できます。 値がその型かどうかは(of-type? value type)で判定されます (型とクラス参照)。

省略可能な引数 message obj … が与えられた場合、 それらはtest-exprが偽を返した際にerrorの引数に使われます。

(define (strlen s)
  (assume-type s <string>)
  (string-length s))

gosh> (strlen 4)
*** ERROR: s is supposed to be of type #<class <string>>, but got 4

(define (strlen s)
  (assume-type s <string> "Argument s must be a string, but got:" s)
  (string-length s))

gosh> (strlen 4)
*** ERROR: Argument s must be a string, but got: 4

このフォームによる情報は、将来のコンパイラで最適化に使われるかもしれません。 コンパイラが型制約の情報を利用するためには、typeが静的に型へと計算可能でなければ なりません。すなわち、クラスか、型コンストラクタか、それらへのconstantな束縛です。

註: assumeと同様、このフォームも勧告として機能します。 チェックがなされるか、exprが評価されるかどうかは保証されません。



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT