それじゃ、id:yagiey:20081114で紹介した6種類のアクションを実装していくことにする。
まずは*const関数。(半角のアステリスク記号は、はてな記法に解釈されるため、ソースコード以外の箇所では全角文字で表記)
早速定義を示すと、
(define *const (lambda (e table) (cond ((number? e) e) ((eq? e #t) #t) ((eq? e #f) #f) (else (build 'primitive e)))))
*const関数は、atom-to-action関数の定義より、以下のアトムに対して返される関数。
- 数値
- #t
- #f
- cons
- car
- cdr
- null?
- eq?
- atom?
- zero?
- add1
- sub1
- number?
つまり、リテラル(って言って良いのかな?)と組込み関数にマッチする関数。
どれを組込み関数にするかは、実装者が決める。ここでは、cons以下の10個の関数を組み込み関数としている。
リテラルの場合はその値を返し、関数ならば
(primitive 関数名)
というリストを返す。
このリストがどう使われるのかは後ほど。
また、この関数は第2引数のテーブルを使用する必要がない。
次に、*identifier関数。
こいつの定義は以下
(define *identifier (lambda (e table) (lookup-in-table e table initial-table))) (define initial-table (lambda (name) (car '())))
*identifier関数は、atom-to-action関数の定義により、識別子にマッチする。
つまり、そのアトムが何かを指し示す名前だった場合にマッチ。
戻り値は、名前が示す値。それを探すのに第2引数のテーブルを使用する。
initial-table関数は、テーブルの中に名前が見つからなかったときに評価される関数。
上の定義だと、必ず実行時エラーで以上終了するようになっている。
これ以降はlist-to-actionから返されるアクション。
最初は*quote。定義は以下。
(define *quote (lambda (e table) (text-of e))) (define text-of second)
これは簡単ね。
リストeの先頭はアトムで、かつ、それがquoteだって分かっているので、eの2番目のS式が返すべき値。
次は*lambda関数。定義は以下。
(define *lambda (lambda (e table) (build 'non-primitive (cons table (cdr e)))))
こいつはlambda式にマッチする。
でも、パッと見、返す値は複雑でよく分からん。
そんなときは、実際にやってみよう。
(*lambda '(lambda (x) (add1 x)) '())
の結果は、
(non-primitive (() (x) (add1 x)))
だと。このリストがどう使われるかは後ほど。
ちなみに、このリストにsecondを適用した結果のリストはclosure recordと呼ばれるそうな。
んで、closure recordから1番目、2番目、3番目のS式を取り出すリストをそれぞれ
(define table-of first) (define formals-of second) (define body-of third)
と定義してる。
次は*cond関数。定義は以下。
(define *cond (lambda (e table) (evcon (cond-lines-of e) table))) (define evcon (lambda (lines table) (cond ((else? (question-of (car lines)) (meaning (answer-of (car lines)) table)) ((meaning (question-of (car lines)) table) (meaning (answer-of (car lines)) table)) (else (evcon (cdr lines) table))))) (define cond-lines-of cdr) (define else? (lambda (x) (cond ((atom? x) (eq? x 'else)) (else #f)))) ;; (S式1 S式2)からS式1とS式2を取り出す補助関数2個 (define question-of first) (define answer-of second)
*cond関数はcondによる条件分岐を処理するためのアクション。
補助関数がいくつもあってややこしいな。
...そんな時は実際にやってみればおk。
(*cond '(cond (coffee klatsch) (else party)) '(((coffee) (#t)) ((klatsch party) (5 (6)))))
を実際にやってみよう(面倒くさいから書かんけど
答えは5ね。
テーブルは、evcon関数で条件などを評価するためにmeaning関数を使用するから必要。
今日はここまで。
次回は*application関数。