structって何だ

以下のコードがいかにも冗長。

      if test.within_circle(x, y, i, j, r) > 0 then 
        old = buf.buf[i][j].r
        buf.buf[i][j].r = (color.r * fore) + (old * back)

        old = buf.buf[i][j].g
        buf.buf[i][j].g = (color.g * fore) + (old * back)

        old = buf.buf[i][j].b
        buf.buf[i][j].b = (color.b * fore) + (old * back)

      end

そうか、こういうときに動的言語のよさってやつ(?)で、レシーバーに送るメッセージのほうをいてレートさせればいいんだと思って、以下のように書き換えた。

      if test.within_circle(x, y, i, j, r) > 0 then 
        [:r, :g, :b].each do |col|
          old = buf.buf[i][j].__send__(col)
          buf.buf[i][j].__send__(col) = (color.__send__(col) * fore) + (old * back)
        end
      end

ところがこれは動かない。Struct固有の問題なのか、何か間違えてるのかよく分からない。struct.cをちらっとのぞくと、structオブジェクトのメンバにアクセスする部分はふつうのメソッドと違ってCでrb_struct_getmemberとか書かれてて何か特殊なのかと思った。でも、

Pixel = Struct.new(:r, :g, :b)
p = Pixel.new(1, 2, 3)
[:r, :g, :b].each do |col|
  puts p.__send__(col)
end
% ruby test.rb
1
2
3

と普通に動く。むむむ。

代入の左辺に来てはいけないような気がしてきた。つまり、明示的にメソッドを送るとCでいう間接参照になってしまって、アドレスを示すポインタのようなものは戻ってきてないんじゃないかしら。

Pixel = Struct.new(:r, :g, :b)
p = Pixel.new(1, 2, 3)
[:r, :g, :b].each do |col|
  p.__send__(col) = 1
end
puts p 
% ruby test.rb
test.rb:4: syntax error, unexpected '=', expecting keyword_end
  p.__send__(col) = 1
% 

なるほど。うーん?

Pixel = Struct.new(:r, :g, :b)
p = Pixel.new(1, 2, 3)
[:r, :g, :b].each do |col|
  p.g = 1
end
puts p 
% ruby test.rb
#<struct Pixel r=1, g=1, b=3>
% 

なるほど。うーむ。