- S式OCaml(Lisp風OCaml)トランスレータ2 : (2011/11/28)
-
一発ネタのつもりだったけど、意外と色々と興味深かったので前回の続きを……
結局はOCamlに変換されるため、見た目はSchemeっぽいがOCamlの機能に関してもいくつか使えるようになっている
関数の部分適用、パターンマッチ、タプルの展開がそれにあたる
(先日の変換スクリプトもちょっと改修してある)
s_exp_ocaml.scm
部分適用
(define foo (lambda (x y) (+ x y)))
(define bar (foo 10))
(print_int (bar 15))
関数fooは引数を2つ受け取りその合計を返す関数だが
一つだけ引数を受け取った場合は、残りの引数を受け取る関数を返す
この例では関数barが残りの一つの引数を受け取る関数に当たる
関数barに1つの引数を与えることで、本来の関数fooの結果が得られる
パターンマッチ
マッチの仕様はOCamlに準拠
例えば整数リストの要素を出力する以下の2つの関数は等価となる
(define print_list_m
(lambda (ls)
(match ls
('() )
(_
(print_int (car ls))
(newline)
(print_list_m (cdr ls)))
)
))
(define print_list_i
(lambda (ls)
(if (= ls '())
()
(begin
(print_int (car ls))
(newline)
(print_list_i (cdr ls)))
)
))
この例では分岐が2つだけなのでそこまで差は無いが、分岐が多いほどパターンマッチの方がスッキリ書けるようになる
タプルの展開
(let
(
(num_x2 (lambda (x y) (tuple (* x 2) (* y 2))))
((rx ry) (num_x2 3 5))
)
(print_int rx)
(print_int ry)
)
letの変数名を記述する部分に複数の値を書くとタプルの内容を展開しながら変数束縛を行う
関数num_x2は2つの引数をそれぞれ2倍して結果をタプルで返す
そのタプルを展開して変数rxとryにて変数束縛を行っている。
この例では2つのint型の数値を返しているが、タプルはリストと違い異なる型同士の組み合わせでも問題ない
これにより実質的に多値のように扱うことができるので、複数の値を返す関数等も書きやすくなる
ちなみにタプルの展開はletだけの機能でdefineに関しては未対応となる
というのも「(define (foo bar) ...)」の記法はSchemeにおいては
「(define foo (lambda (bar) ...))」の省略形なので……
またlambdaも拡張して引数のタプルを展開する機能をつけてある
これでcomposeによる関数合成で、複数の引数を受け取る関数の合成を多少書きやすくなる
以下が2つの値をタプルで受け取り、それを展開してそれぞれ2倍し、またタプル化して返す関数
(lambda ((x y)) (tuple (* x 2) (* y 2))
composeは関数を合成し、新たな関数を作る
(define new_func (compose f1 f2 f3))
(new_func argv)
これは「(f1 (f2 (f3 argv)))」と同じ結果となる
つまり注意しなければならないのは「f1->f2->f3」ではなく
「f3->f2->f1」の順序で関数が適用されるという点
通常関数の戻り値は一つだが、タプルを利用することで複数の値を受け取り、複数の値を次の関数に渡すことが可能になる
(関数合成は特にOCamlの言語仕様には無い、実装は可能だけど。
ちなみにHaskellには言語仕様に組み込まれてて専用の演算子まである)
そういやOOP関連がまだだったのでトランスレータ関連のネタはあと一回続く
あとマクロについてもちょっと書くかも
(まあS式化することで得られる最大の恩恵はLispマクロが使えるようになることなので本当はマクロに一番力を注ぐべきなんだろうけど)