2008-01-06
■ [biwascheme] letrecとletrec*
3行でまとめると
- letrecは相互再帰関数を定義するためのlet
- letrec*はトップレベルの意味を明確にするために導入されたもの
- letrec*はバインディングの値部分で、それより前の変数を参照してよい。letrecはダメ
ということみたい。
letrec*はR6RSで正式導入されたものなので、R5RSにはない。
以下R6RSのてきとーな概訳:
11.4 - letrec
- (letrec <bindings> <body>) syntax
- <bindings> は ((<variable1> <init1>) ...), initは式
- <body>は11.3で説明されている
- variableには同じ変数が複数回出てきてはいけない
- 各variableが新しい場所に束縛される
- その環境下で、各initが評価される(順序は不定)
- 各variableが対応したinitの評価結果に束縛される
- その環境下で、bodyが評価される
- body中の最後の式の評価結果が返される
各variableの有効範囲はletrec式の全体であり、これによって相互再帰関数を定義することができる。
例
(letrec ((even? (lambda (n) (if (zero? n) #t (odd? (- n 1))))) (odd? (lambda (n) (if (zero? n) #f (even? (- n 1)))))) (even? 88)) ⇒ #t
各initは、どのvariableの値も代入・参照することなしに評価できるべきである。 letrecの最もよくある使い方においては、全てのinitはlambda式なので、この条件は自動的に満たされる。 また、各initの継続は一度以上実行されるべきではない。
処理系の責任:処理系は、initの評価中にvariableが参照されたことを検出しなければならない。もし処理系がこのような違反を検出したなら、 例外&assertionを投げるべきである。initの継続が2回以上呼び出されたかどうかは、検出してもしなくても良い。が、検出するならば 例外&assertionを投げなければいけない。
11.4 - letrec*
- (letrec* <bindings> <body>)
- <bindings>は((<variable1> <init1>) ...), initは式、<body>は11.3参照。各variableは被っちゃダメ
- 各variableが新しい場所に束縛される
- 各variableが左から順に、対応したinitの評価結果に束縛される
- この環境下でbodyが評価され、bodyの最後の式の評価結果が返される
評価・代入は左から順に行うが、各variableの束縛自体はletrec*式全体で有効であり、これによって相互再帰関数が定義できる。
(letrec* ((p (lambda (x) (+ 1 (q (- x 1))))) (q (lambda (y) (if (zero? y) 0 (+ 1 (p (- y 1)))))) (x (p 5)) (y x)) y) ⇒ 5
各initは対応するvariableや、それ以降のvariableの値を参照・代入することなしに評価できなければならない。 また、各initの継続は2度以上実行されるべきではない。
処理系の責任:処理系はinitの評価中に、対応するvariableやそれ以降のvariableの値の参照を検出しなければならない。検出したら、 例外&assertionを投げなければならない。継続の方はチェックしてもしなくてもいいが、するなら例外&assertionを投げなければならない。