さて、本を読み進めるうちに書くコードも長くなってきた。たとえば前のエントリで書いた関数 o= は32行ある。だけど、本質的な部分はテスト用コードと空行を含めても11行だ。残りは、o= を作るための補助的というか基本的な関数のコードで占められている。こういうコードは冗長なだけでなく、コードを見難くする。
というわけで、基本的な関数はモジュールにして外に追いやりたいと思ったので、Gauche でのやり方を調べてみた。
cf. 4.11 モジュール ― Gauche ユーザリファレンス
cf. 3.1 Gaucheを起動する ― Gauche ユーザリファレンス
簡単な作り方と使い方をまとめると、次のようになる。
- define-module を使ってモジュールを定義し、中に関数の定義を書く。
- 上記の中で export-all を使って、定義した関数をエクスポートしておく。
- 適当なファイル名で保存する。
- モジュールを使う側のファイルで、(use モジュール名) とする。
- gosh を起動するときに -I. オプションをつける。”.” はモジュールがカレントディレクトリにある場合。
で、今まで書いた基本的な関数をまとめたモジュールがこれ。モジュール名は mymodule とした。
(define-module mymodule (define atom? (lambda (x) (and (not (pair? x)) (not (null? x))))) (define lat? (lambda (l) (cond ((null? l) #t) ((atom? (car l)) (lat? (cdr l))) (else #f)))) (define add1 (lambda (n) (+ n 1))) (define sub1 (lambda (n) (- n 1))) (define o+ (lambda (n m) (cond ((zero? m) n) (else (add1 (o+ n (sub1 m))))))) (define o- (lambda (n m) (cond ((zero? m) n) (else (sub1 (o- n (sub1 m))))))) (define o* (lambda (n m) (cond ((zero? m) 0) (else (o+ n (o* n (sub1 m))))))) (define o> (lambda (n m) (cond ((zero? n) #f) ((zero? m) #t) (else (o> (sub1 n) (sub1 m)))))) (define o< (lambda (n m) (cond ((zero? m) #f) ((zero? n) #t) (else (o< (sub1 n) (sub1 m)))))) (define o= (lambda (n m) (cond ((o> n m) #f) ((o< n m) #f) (else #t)))) (export-all))
テスト用スクリプト:
(use mymodule) (print (atom? 'a)) (print (lat? '(my name is takatoh))) (print (lat? '(my name (is) takatoh))) (print (o+ 2 5)) (print (o- 10 4)) (print (o* 3 3)) (print (o> 12 8)) (print (o< 7 10)) (print (o= 6 6))
試してみよう。
^o^ > gosh -I. test_mymodule.scm #t #t #f 7 6 9 #t #t #t
うまくいってるようだ。
このモジュールは、スクリプトモードだけでなくインタラクティブモードでも使える。
^o^ > gosh -I. gosh> (use mymodule) # gosh> (atom? 'a) #t gosh> (lat? '(hello scheme)) #t gosh> (o= 3 7) #f