2007-10-07
■ 休載
多忙につきしばらくお休みします。
Gaucheを学びたい人へ。単純な問題をクックブックにあまり載せなかったのは、リファレンスマニュアルを読めばわかることは書く必要がなかったという理由ですので、まずマニュアルを読み始めることをお勧めします。一度見てみれば、マニュアルが充実していることに気づくはずです。
再開するつもりなので、RSSリーダに登録している人はそのままで。
2007-10-04
■ コンパイル時に文字列を連結する
問題
複数行の長い文字列をソースコードに行ごとに分割して書きたいが、実行時にstring-joinをするのはコストが気になる。すべての文字列の内容はコンパイル時に決まっているので、コンパイル時に文字列の連結ができないか。
答え
トラディッショナルなマクロを使うとコンパイル時に文字列を連結できます。下のjoinはコンパイル時に文字列を連結するマクロです。
(define-macro (join . strs) (string-join strs "\n"))
解説
マクロの特徴の1つはそれがコンパイル時に展開されることです。Cのマクロがプリプロセッサ(CPP)に処理されてからコンパイルされるように、Schemeのマクロはマクロ展開が行われてからその結果がコンパイルされます。従ってマクロで文字列を連結すると、コンパイラには連結された文字列が渡されます。実行時にコストがかかりません。
コンパイル結果をディスアセンブラ(disasm)で動作を確認してみます。まずは上で定義したjoinマクロを使うコードを見てみます。コンパイル時に決定した文字列が実行時にそのまま返っているのがわかります。
(disasm (lambda () (join "abc" "def"))) ;; 0 CONST-RET "abc\ndef"
一方、こちらは実行時にstring-joinで文字列を連結するコードです。実行時に文字列をスタックにプッシュし、手続きを呼び出す命令列にコンパイルされています。
(disasm (lambda () (string-join '("abc" "def") "\n"))) ;; 0 CONST-PUSH ("abc" "def") ;; 2 CONST-PUSH "\n" ;; 4 GREF-TAIL-CALL(2) #<identifier user#string-join>; (string-join '("abc" "def") "\n") ;; 6 RET
なお、コンパイラの最適化が進んで、文字列連結の定数畳み込みが行われるようになれば、こういったテクニックは不要です。従ってマクロで書けば速くて、実行時に評価されるように書くと遅いとは一概には言えません。それに、タイトなループの中でなければ文字列の連結コストは無視できる程度のはずです。仕組みを理解したうえで最適化しましょう。
参照
リファレンスマニュアル: 伝統的なマクロ、string-join
2007-10-01
■ 実行中のスクリプトファイルの名前を得る
問題
スクリプトファイル名を取得する方法は?
答え
mainに渡されるリストの第1要素がスクリプトのファイル名です。
#!/usr/bin/gosh (define (main args) (print (car args)) ; スクリプトファイル名を表示 0)
*program-name*というグローバル変数もスクリプトファイル名に束縛されているので、こちらを使ってもいいのですが、main手続きの引数はきちんとSRFI 22で定義されているので、移植性を重視するならmainの引数を使うのがよいでしょう。
