[R7RS base] 各変数varがexprの値に束縛されているローカルな環境を作成し、 その中でbody …を評価します。varはシンボルでなければ ならず、重複があってはなりません。body …の最後の式の値が このフォームの値となります。
これらの4つのフォームの違いは、exprが評価される時点のスコープと順序にあります。
let
はexprをletフォームに入る前の環境において評価します。
各exprの評価される順序は不定で、コンパイラは最適化のために自由に順序を
変更することがあります。
一方、let*
はexprを現れた順に、
それ以前のvarが束縛された環境において評価してゆきます。
letrec
は全てのvarが仮想的に不定の値に束縛された環境において
各exprを(順不同で)評価します。
letrec
は相互再帰的なローカル関数を定義する場合に必要です。
最後に、letrec*
はletrec
と同じスコープ規則を使い、
さらにexprを現れる順に評価するものです。
(define x 'top-x) (let ((x 3) (y x)) (cons x y)) ⇒ (3 . top-x) (let* ((x 3) (y x)) (cons x y)) ⇒ (3 . 3) (let ((cons (lambda (a b) (+ a b))) (list (lambda (a b) (cons a (cons b 0))))) (list 1 2)) ⇒ (1 2 . 0) (letrec ((cons (lambda (a b) (+ a b))) (list (lambda (a b) (cons a (cons b 0))))) (list 1 2)) ⇒ 3
ひとつのexprを評価するために、それまでに現れたvarの値を参照する
必要がある時は、letrec*
を使わなければなりません。
下の例では、aとbの値を計算するためにcubeの値を
使っているので、letrec*
にする必要があります。
(上の例との違いに注意してください。上の例では、listの値を計算する
時には、同時に束縛されているconsの値は必要とされません。consの値が
必要になるのはlistが実際に適用される時です。)
(letrec* ((cube (lambda (x) (* x x x))) (a (+ (cube 1) (cube 12))) (b (+ (cube 9) (cube 10)))) (= a b)) ⇒ #t
この例は現在のGaucheではletrec
を使ってもたまたま動作しますが、
将来にわたって動作し続けることは保証されません。
letrec
を使うなら評価順に依存しないように、プログラマが気をつけないといけません。
振り返ってみれば、letrec*
だけが提供されてた方が単純だったでしょう。
生憎、Schemeの歴史の中ではletrec
の方がずっと前からあったので、いまさら
取り除くこともできないのです。また、letrec*
に出来ない最適化が
letrec
だと可能になることもあります。
変数が一つしか無い場合の便利なマクロです。次のように展開されます。
(let ((var expr)) body …)
このマクロは次のようなイディオムを簡素化します。
(let1 var expr (if var then else))
このマクロは次のようなイディオムを簡素化します。
(let1 var expr body … var)
[SRFI-2]
簡単に言うと、このフォームはlet*
のように動作しますが、
bindings中の式が#f
に評価されたらそこで評価を打ち切り
#f
を返します。
各bindingは以下のいずれかの形式でなければなりません。
(variable expression)
expressionが評価されます。それが真の値を返したら、その値がvariable
に束縛され、次のbindingへと進みます。もうbindingが無ければ
body …が評価されます。もしexpressionが#f
を返したら、
評価を打ち切り、and-let*
から#f
を返します。
(expressionx)
この形式ではvariableが省略されています。Expressionが評価され、 その結果は評価を続行するか打ち切るかを判断するためにのみ使われます。
bound-variable
この形式ではbound-variableは束縛変数を示す識別子でなければなりません。 その変数の値が偽でなければ評価を続行します。
いくつか例を挙げます。次のコードは連想リストalistからkeyを 探し、見つかったらその値を返します。
(and-let* ((entry (assoc key alist))) (cdr entry))
もしargが正確な整数の文字列表現だった場合はnumを返し、そうでなければ 0を返します:
(or (and-let* ((num (string->number arg)) ( (exact? num) ) ( (integer? num) )) num) 0)
以下のコードはとあるサーバーのポート番号をいくつかの可能性 (環境変数、設定ファイル…)の中から探す仮想的なコードです。
(or (and-let* ((val (sys-getenv "SERVER_PORT"))) (string->number val)) (and-let* ((portfile (expand-path "~/.server_port")) ( (file-exists? portfile) ) (val (call-with-input-string portfile port->string))) (string->number val)) 8080) ; default
testを評価し、それが#f
でなければvarをその値に束縛して
exp1 exp2 …を評価します。戻り値は最後の式の値です。
testが#f
だった場合は単に#f
を返します。
これはand-let*
やif-let1
を使って次のとおり書くこともできます。
しかし、このパターンを書くことがあまりに多いため、専用のマクロを用意する価値が
あると判断しました。
(and-let1 var test exp1 exp2 …) ≡ (and-let* ([var test]) exp1 exp2 …) ≡ (if-let1 var test (begin exp1 exp2 …) #f)
動的スコープの変数をエミュレートするマクロです。
varはfluid-let
フォームを含むスコープで定義されている
変数でなければなりません。valは式です。
fluid-let
はまずvalを評価し、
valをvarに動的スコープで束縛してbody … を評価します。
マルチスレッド環境下では、varの値の変化は全てのスレッドから見えます。 このフォームは主として他の処理系のコードを移植する際の利便性のために 追加されました。スレッドローカルな動的状態を実現するには、 パラメータオブジェクト(パラメータ参照)を 使って下さい。
(define x 0) (define (print-x) (print x)) (fluid-let ((x 1)) (print-x)) ⇒ ;; prints 1
[SRFI-8] この構文により、多値を受け取ることができます。 formalsはシンボルのリストです。不完全なリストであっても構いません。 expressionが評価され、返された値がlambda形式の引数の束縛と 同じようにしてformals内の変数と束縛され、その環境下でbody …が 評価されます。
(define (divrem n m) (values (quotient n m) (remainder n m))) (receive (q r) (divrem 13 4) (list q r)) ⇒ (3 1) (receive all (divrem 13 4) all) ⇒ (3 1) (receive (q . rest) (divrem 13 4) (list q rest)) ⇒ (3 (1))
なお、多値のcall-with-values
は
receive
と等価な手続き的インタフェースです。
多値を複数の変数に同時に束縛するには、define-values
(定義参照) が使えます。
また、下のlet-values
と
let*-values
はlet
のような形式で多値を扱うことができます。
[R7RS base]
vars は変数のリストです。expr は評価され、最初の返り値は、
vars の最初の変数に束縛されます。二番目の返り値は、vars の
二番目の変数に束縛され、以下同様です。そのあと、body が評価されます。
expr のスコープは let
と同様に、let-values
の
外側になります。
(let-values (((a b) (values 1 2)) ((c d) (values 3 4))) (list a b c d)) ⇒ (1 2 3 4) (let ((a 1) (b 2) (c 3) (d 4)) (let-values (((a b) (values c d)) ((c d) (values a b))) (list a b c d))) ⇒ (3 4 1 2)
vars lambda パラメータと同様に、ドット付リストでも、 単一のシンボルでもかまいません。
(let-values (((x . y) (values 1 2 3 4))) y) ⇒ (2 3 4) (let-values ((x (values 1 2 3 4))) x) ⇒ (1 2 3 4)
expr によって返された値の数と vars が期待する数とが一致しない 場合にはエラーになります。
[R7RS base]
let-values
と同じですが、各 expr のスコープが先行する vars
を含みます。
(let ((a 1) (b 2) (c 3) (d 4)) (let*-values (((a b) (values c d)) ((c d) (values a b))) (list a b c d))) ⇒ (3 4 3 4)
[SRFI-31] 再帰的な参照のある式の評価を行うマクロです。
最初の形式は、varがexprの結果に束縛される状態でexprを 評価します。 2番目の形式は以下の形式と等価です。
(rec name (lambda vars expr …))
例:
;; constant infinite stream (rec s (cons 1 (delay s))) ;; factorial function (rec (f n) (if (zero? n) 1 (* n (f (- n 1)))))