FileIOとブロック

RubyはFileIOもスマートだ。
ファイルの内容を単純にダンプするコードを書く場合、方法は幾つかあるが、Fileクラスを使った場合他の言語同様に

  • fileio.rb
f = File.open('atagoyama.txt')
begin
  while l = f.gets
    puts l
  end
ensure
  f.close
end

などと書ける。

  • 実行結果
>ruby fileio.rb
祇園町を西へ西へ、鴨川を渡ります、堀川も打ち過ぎまして二条のお城を後目に殺し、
どんどんどんどん出てまいりますというと野辺へかかってまいります。
何しろ春先でございます、空には雲雀がピーチクパーチクとさえずっていようか、
野には陽炎が燃えていようかという。

後処理(ファイルクローズ)を含めてもこれで十分にシンプルなだが、このままではなにかが足りないし、Rubyぽくない。
Rubyと言えばブロックだが、当然Fileクラスのメソッドもブロックを受け取ることができ、上記の処理は以下のようにも書ける。

  • fileio.rb 改
File.open('atagoyama.txt') do |file|
  while l = file.gets
    puts l
  end
end

おっと、ファイルの内容の取得(行、又は文字の取得)も同クラスのeachメソッドでブロック化してしまおう。

  • fileio.rb 改2
File.open('atagoyama.txt') do |file|
  file.each_line { |line| puts line }
end

処理の内容にいささかの省略も無いのに、行数はついに3行になってしまった。

上記のブロック対応後のコードはファイルのクローズを書いていないが、これは別に忘れている訳ではない。
Fileクラスのopenメソッドはブロックを受け取った場合、内部でブロックを実行した後に自動的にcloseメソッドを呼び出すため、わざわざクローズを書く必要が無いのだ。

openメソッドの中身は恐らくはこんなイメージの実装になっていると思われる。(本当の実装はもっといろいろな処理が入っていると思うが省略)

f = File.new(filename)
if block_given?
  begin
    yield f
  ensure
    f.close
  end
end

Ruby初心者の私から見たRubyのブロックというのは、Javaの無名インナークラスであり、C#のデリゲートであり、イベントであり、更にはAOPの組込み実装である。(Rubyをメインで使っているプログラマはそのような比喩の仕方はしないだろうが、私には語彙が無い)

ブロックにより実行コードの挿入やクラスの拡張がこうも簡単にできる訳だが、巷でAOPが話題に上った時でも、Ruby界隈ではあまり騒いでいなかったのもこれで判るというものだ。