読者です 読者をやめる 読者になる 読者になる

【Ruby】動的プログラミング3

前回の復習

method_missingとは?
→呼び出したメソッドが存在しない時に実行されるメソッド。例外が呼び出される。通常はBasicObjectクラスで実装されているがオーバーライドすることで振る舞いを変えられる。クラスの継承の中で親クラスがオーバーライドしている可能性もあるため、ある程度メソッド名を絞って実装するのがよい。

7-3 eval

なんだそれ?
→evaluate(式評価)の略。式評価で何を評価しているかというと「その式がある環境で実行された時に、どんな意味を持っているか」ということ。evalのためのメソッドには以下の様なものがある。

eval 'p self'

object = Object.new
# レシーバのオブジェクトをselfとして式を評価
object.instance_eval {p self}
# レシーバのクラスをselfとして式を評価
object.class.class_eval {p self}

どうやって使うの?
→実行する環境によってselfが変わるため、メソッドを作るメソッドを作ることができる。あんまりピンとこないためサンプルコードを見る。

class LoggingInstanceVariable
  logging_instance_val_names = %w(first_val second_val third_val)

  logging_instance_val_names.each do |val_name|
    eval <<-END_OF_DEF
      attr_reader :#{val_name}, :before_#{val_name}

      def #{val_name}=(val)
        @before_#{val_name} = @#{val_name}
        @#{val_name} = val
      end
    END_OF_DEF
  end
end

obj = LoggingInstanceVariable.new

obj.first_val = 1

p obj.first_val
p obj.before_first_val

obj.first_val = 2

p obj.first_val
p obj.before_first_val

値がインスタンス変数に代入されたら、一個前の値だけ保存しておくという手続きをevalを使ってまとめたもの。
Javaだったらクラスを作ってデータをまとめて…とするのだろうけど、それじゃ過剰設計だというときに使えそう。

また、動的にメソッドの追加もできる。

class AttrClass
  def initialize
    @attr = "attr"
  end

  def add_reader(instance_val_name)
    eval <<-END_OF_DEF
      def #{instance_val_name}
        @#{instance_val_name}
      end
    END_OF_DEF
  end

  def add_writer(instance_val_name)
    eval <<-END_OF_DEF
      def #{instance_val_name}=(val)
        @#{instance_val_name} = val
      end
    END_OF_DEF
  end

end

attr_obj = AttrClass.new
p attr_obj #=> 
attr_obj.respond_to? "attr" #=> false
attr_obj.add_reader "attr"
attr_obj.respond_to? "attr" #=> true
p attr_obj.attr #=> attr

特異メソッドだけでなく普通のメソッドを普通のクラスに後から追加するなんてこともできてしまう。
まだなれていないためとても気持ち悪い!
class_eval、instance_eval等evalメソッドには他にも種類があるが、とりあえずとばす。