さて、本を読み進めるうちに書くコードも長くなってきた。たとえば前のエントリで書いた関数 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