昨日の記事

ympbyc.hatenablog.com

の内容をScheme (gauche)で実験できるようにしてみます。

今回の記事は、上から順にGaucheのリスナに貼り付けていけば、動作が確認できるようにしてあります。

準備

まず、固定長引数とそれ以降の引数を取って、余った引数を結果にapplyする関数を作るマクロを作ります。define-syntax面倒臭いのでdefine-macroしちゃいます。

( define-macro defix ( lambda ( defclause . body ) `( define (,@ defclause . next ) ( if ( pair? next ) ( apply ( begin ,@ body ) next ) ( begin ,@ body ))))) ( macroexpand '( defix ( -car x xs ) x )) ( define-macro lamfix ( lambda ( params . body ) `( lambda (,@ params . next ) ( if ( pair? next ) ( apply ( begin ,@ body ) next ) ( begin ,@ body ))))) ( macroexpand '( lamfix ( x xs ) xs ))

ちょっと汚いけどあんまり気にしないで。

以降、すべての define を defix , lambda を lamfix に置き換えて定義していきます。

リスト関数

( defix ( cons x xs ) ( lamfix ( f ) ( f x xs ))) ( defix ( car xs ) ( xs ( lamfix ( y ys ) y ))) ( defix ( cdr xs ) ( xs ( lamfix ( y ys ) ys ))) ( defix ( -car x xs ) x ) ( defix ( -cdr x xs ) xs ) ( defix ( <: x xs ) ( lamfix ( y ) ( cons y ( cons x xs )))) ( defix ( list x ) ( cons x '())) ( list 4 <: 3 <: 2 <: 1 -car ) ( list 4 <: 3 <: 2 <: 1 -cdr -cdr -car )

map や filter の例も同様に、 defix や lamfix をうまいこと使って作っていきます。

( defix ( map xs f ) ( if ( null? xs ) '() ( cons ( f ( car xs )) ( map ( cdr xs ) f )))) ( defix ( filter xs f ) ( if ( null? xs ) '() ( if ( f ( car xs )) ( cons ( car xs ) ( filter ( cdr xs ) f )) ( filter ( cdr xs ) f )))) ( defix ( prefix f ) ( lamfix ( x xs ) ( pa$ f ( cons x xs )))) ( define -map ( prefix map )) ( define -filter ( prefix filter )) ( list 5 <: 4 <: 3 <: 2 <: 1 -filter odd? -map ( pa$ * 2 ) -map print )

-> も簡単です。

( defix ( -> x ) ( lamfix ( f ) ( lamfix ( g ) ( f ( g x ))))) ( defix ( <- x ) x ) ( -> 2 -> ( pa$ + 3 ) -> ( pa$ * 4 ) <- number->string )

できました！

混ぜて使う

固定長引数の関数は defix で定義して、可変長引数の関数は define で定義すれば、混ぜて使うこともできます。

( define ( list . xs ) ( fold-right ( lambda ( x acc ) ( cons x acc )) '() xs )) ( -> ( list 1 2 3 4 ) -> ( cut map <> ( pa$ * 2 )) <- ( cut map <> print ))

まとめ

Scheme処理系の上に、マクロを使って評価規則をちょっとだけ変えたLispを実装しました。Lisp式のマクロがあると言語の(見かけ上の)評価規則すら変えられるという例です。

リストの例(consの改造)は影響が大き過ぎるので実用は難しいと思いますが、固定長引数の関数を新しく作る時にとりあえず defix を使ってみるっていうのはやってみると意外と実用的かもしれません。