L2 Lisp 7.3 と Ruby 1.9.0 の再評価
2008.7.4 (鈴)- 前回 現行の各種 Python への L2 Lisp の移植 (7.2 版/Python) 2008.6.10
- 1. はじめに (7.3 版/Ruby)
- 2. 行編集機能の実現
- 3. Ruby 1.9.0 の再評価: ruby 1.9.0-2 の高速性
- 4. おわりに
- 次回 C# による L2 Lisp の実装 (8.0-8.2 版/C#) 2008.7.18 - 2008.8.28
- L2Lisp.rb 7.3 版, UTF-8, LF, 1433 行, 40811 バイト, md5: 91a64793daf38fdfd7bf27c4db966c1e
- 上記の Web 閲覧用ファイル (HTML 形式)
1. はじめに
L2 Lisp 7.3 は, Ruby 用 L2 Lisp 7.0 を Python 用 L2 Lisp 7.2 に準じて以下の点で改訂したものである。
- 対話セッションでの行編集機能
- 対話セッションでの Control-C 打鍵の捕捉
(exit n)関数の追加(equal x y)関数の訂正
また,独自の改良として,Ruby 言語との相互作用のために用意した
ruby-send, ruby-send-apply
両関数で send メソッドにかえて __send__ メソッドを使うように改めた。
ruby 1.8.2, 1.8.5, 1.8.6, 1.8.7, 1.9.0 と jruby 1.1.2 で動作を確認した。
本稿は,まず,L2 Lisp 7.3 の主要な改訂点である対話セッションでの行編集機能を Ruby でどのように実現したか記述する。
「より実用的な L2Lisp.rb」§5 と 「L2 Lisp の Python 2.5 & 3.0 への移植」§4 に見るように ruby 1.9.0-1 は従来の ruby に比べ L2 Lisp の実行速度が劣っていた。 しかし,ruby 1.9.0-2 が発表されたのを機に再評価したところ,意外な事実が判明した。 本稿は,次に,この評価とその結果について述べる。
2. 行編集機能の実現
irb などに使われている Ruby の履歴付き行編集機能は,readline
で提供されている。
require 'readline' をした後,STDIN.gets の代わりに
(戻り値の文字列から改行文字が省かれることに注意して) Readline.readline(prompt, true) を呼び出せばよい。
L2 Lisp 7.3 では,gets メソッドが呼び出されると内部で
Readline.readline を実行するような疑似ファイル・クラス InteractiveInput を定義し,
そのインスタンスで STDIN を代替することにより,この機能を利用する。
ただし,Mac OS X 10.4 標準添付の ruby 1.8.2 のように,readline がない場合もある。
その場合は,内部で STDIN.gets を実行するように疑似ファイル・クラスを定義する。
下記に実際のコードを示す。
begin require 'readline' rescue LoadError # ロード失敗 → 行編集機能なしのクラス定義 class InteractiveInput < InteractiveInputBase def gets # 初回は1次,以降2次プロンプトで1行を入力する if @primary STDOUT.print @ps1 @primary = false else STDOUT.print @ps2 end STDOUT.flush @lineno += 1 return STDIN.gets end end # InteractiveInput else # ロード成功 → 行編集機能ありのクラス定義 class InteractiveInput < InteractiveInputBase def gets if @primary prompt = @ps1 @primary = false else prompt = @ps2 end @lineno += 1 s = Readline.readline(prompt, true) return (s.nil?) ? nil : s + "\n" end end # InteractiveInput end
ここで InteractiveInputBase の定義は次のとおりである。
# 場合分けの共通部分をになう基底クラス class InteractiveInputBase attr :lineno # 現在の行番号 def initialize(ps1, ps2) # 引数は1次プロンプトと2次プロンプト @ps1 = ps1 @ps2 = ps2 @primary = true @lineno = 0 end def reset # プロンプトを1次に戻す @primary = true end def close # 何もしない end end # InteractiveInputBase
Interp#run メソッドは,InteractiveInput クラスを使って次のように定義される。
ここでは,今回の改訂点の一つである rescue Interrupt 節による Control-C 打鍵の捕捉にも注目されたい。
# IO または String から式の並びを読んで評価する。
# 無引数ならば対話的に入力/評価/出力を繰り返す。
def run(rf=nil)
if interactive = rf.nil?
rf = InteractiveInput.new('> ', ' ')
elsif String === rf
rf = StringIO.new(rf)
end
rr = Reader.new(rf)
result = nil
loop {
begin
x = rr.read
if x == S_EOF
puts 'Goodbye' if interactive
return result
end
result = eval(x)
puts LL.str(result) if interactive
rescue Interrupt => ex # (典型的には) Control-C が打鍵されたとき
raise unless interactive
puts "\n" + ex.inspect
rescue EvalError => ex
raise unless interactive
print ex
end
}
end
ただし,疑似ファイル・クラス InteractiveInput による抽象化は完全ではない。
2次プロンプトから1次プロンプトに戻すため,Lisp 式を読み取る Reader#read
メソッドで陽に InteractiveInput#reset を呼び出す必要がある。
def read
begin
_read_token
return _parse_expression
rescue SyntaxError => ex
@buf.clear # その行の残りのトークンを捨てて次回の回復を図る
raise EvalError, 'SyntaxError: %s -- %d: %p' % [ex, @rf.lineno, @line]
ensure
@rf.reset if InteractiveInput === @rf
end
end
3. Ruby 1.9.0 の再評価: ruby 1.9.0-2 の高速性
今回の改訂に先立ち ruby 1.9.0-2 が発表された。そこで Mac OS X 10.4.11
(MacBook Pro, Core Duo 2GHz, 1GB メモリ)
上で各バージョンの ruby を改めて構築し,その L2 Lisp 7.3 での速度を比較した。
インストール先を切り分けるために ./configure で --prefix=
を指定した以外,構築パラメタはすべてデフォルトのままである。
起動時間を含めた実行時間をそれぞれ 3 度 time で計り,最短値を採った。
| Ruby | farmer-and-….l | tak.l | 8queens.l |
|---|---|---|---|
| ruby 1.8.5-p231 | 0.272 [s] | 8.172 [s] | 21.681 [s] |
| ruby 1.8.6-p230 | 0.270 [s] | 8.306 [s] | 21.944 [s] |
| ruby 1.8.7-p22 | 0.278 [s] | 8.413 [s] | 22.199 [s] |
| ruby 1.9.0-0 | 0.237 [s] | 4.110 [s] | 10.782 [s] |
| ruby 1.9.0-1 | 0.358 [s] | 13.842 [s] | 36.801 [s] |
| ruby 1.9.0-2 | 0.229 [s] | 4.040 [s] | 10.458 [s] |
これから分かるように ruby 1.9.0-2 が最も高速であり,ruby 1.9.0-0 がこれに次ぐ。
実行時間が極度に短い (したがって起動時間を無視できない)
farmer-and-wolf-etc1.l のテストを除けば,両者は ruby 1.8 シリーズに比べ約2倍の速度である。
一方,ruby 1.9.0-1 は,今回評価した中では,あらゆる点で最低速である。
4. おわりに
Ruby 用 L2 Lisp の対話入力に行編集機能を与えるとともに,Control-C の打鍵で対話セッションが中断しないようにした。 これは Lisp 言語そのものの強化ではないが,処理系の日常的な使いやすさを改善する。
また,この L2 Lisp の実行時間で計った,つまり,小さなベンチマーク・テストではなく,より現実的な規模のプログラムで見た ruby 1.9.0-0 と 1.9.0-2 の高速性を確認した。 「より実用的な L2Lisp.rb」§5 と 「L2 Lisp の Python 2.5 & 3.0 への移植」§4 に見られた ruby 1.9.0-1 の悲惨な低速さは,ruby 1.9 シリーズ全般ではなく,今のところ,その版だけの現象である。
次回へつづく


