『pnmフォーマットファイルをCLOSで作ってみよう』のRubyへの翻訳
http://d.hatena.ne.jp/ytakenaka/20070620/p1
http://d.hatena.ne.jp/ytakenaka/20070624/p1
CLOSの理解のためRubyに翻訳してみました。
module RubyPNM # Rubyのモジュールは名前空間も管理 module PNMDataInfo # インスタンスを持たないクラスはモジュール attr_accessor :width, :height, :depth # 一発アクセサ宣言 def init_data_info(width, height, depth=255) self.width = width self.height = height self.depth = depth end end module PNMHeader include PNMDataInfo # モジュールの継承 attr_accessor :id, :comment def comment=(sequence) # set_hoge(obj, v)は obj.hoge=v と書くのがRuby Way @comment = "# " << sequence end def write_pnm(filename) open(filename, "w") do |stream| stream << "%s\n" % id stream << "%s %s\n" % [width, height] stream << "%s\n" % comment if comment stream << "%s\n" % depth end end end module PNMData include PNMDataInfo attr_accessor :data def init_data(width, height, depth=255) init_data_info(width, height, depth) self.data = Array.new(width) { Array.new(height) } # width x height の二次元配列 end def set_data(x, y, value) case value when Array if value.length == 3 # 真意がわかればビットシフト演算とビットOR演算に翻訳できるよね # (mapcar #'(lambda(v p) # (setf (ldb (byte 8 p) (aref data x y)) v)) # value (list 0 8 16)) data[x][y] = value[0] | value[1] << 8 | value[2] << 16 else raise TypeError end when Fixnum data[x][y] = value else raise TypeError end end end class PPMBin # インスタンスを持つクラスはclassで include PNMHeader include PNMData def initialize() self.id = :P6 end def write_pnm(filename) # :beforeメソッドを呼ぶんじゃなくてsuperclassのメソッドを呼ぶ。 # call-next-methodみたいなの。 super open(filename, "a") do |stream| height.times do |y| # dotimes width.times do |x| pix = data[x][y] # バイナリの読み書きにはperlでおなじみpack/unpack。 stream.write [pix.rgb_red, pix.rgb_green, pix.rgb_blue].pack("C*") end end end end end class PPMText include PNMHeader include PNMData def initialize() self.id = :P3 end def write_pnm(filename) super open(filename, "a") do |stream| height.times do |y| width.times do |x| pix = data[x][y] stream.printf "%d %d %d " % [pix.rgb_red, pix.rgb_green, pix.rgb_blue] end stream.puts end end end end end # こともあろうにfixnumにメソッドを追加しちゃることに class Fixnum def rgb_red self & 0xFF # (ldb (byte 8 0) ,pix) end def rgb_green (self >> 8) & 0xFF # (ldb (byte 8 8) ,pix) end def rgb_blue self >> 16 # (ldb (byte 8 16) ,pix) end end if __FILE__==$0 include RubyPNM bin = PPMBin.new bin.init_data 100, 100 bin.comment = "This is binary format." text = PPMText.new text.init_data 100, 100 text.comment = "This is text format." 100.times do |i| 100.times do |j| args = [j, i, [2*i, 2*j, i+j]] bin.set_data *args text.set_data *args end end bin.write_pnm "ruby.bin.ppm" text.write_pnm "ruby.text.ppm" end
んー、Common Lispだと1.44倍も冗長なんだなぁ…。
$ ls -l pnm.lisp pnm.rb -rw-r--r-- 1 rubikitch users 3676 2007-06-25 01:34 pnm.lisp -rw-r--r-- 1 rubikitch users 2547 2007-06-25 01:48 pnm.rb