Ruby のメタプログラミングでカウンターを作る
Ruby はインクリメントやデクリメントがないのですが、遊びでカウンターを作ってみました。メタプログラミングを使って、呼び出されるたびに step(デフォルトは 1)だけ増える数を与えるメソッドを生成します。「メソッド」というのが工夫したところです。
使い方は、Counter.make(:inc)
でカウンター inc を作成します。カウンターの名前は何でも構いません。デフォルトの初期値は 0、step は 1です。
Counter.make(:inc) 5.times {puts inc} #=>0 1 2 3 4 Counter.make(:count, 100, -5) puts count #=>100 puts count #=>95 puts count #=>90 puts inc #=>5
こんな感じです。特徴としては、トップレベルのメソッドとして定義されるので、クラスのインスタンス・メソッドの中などへ飛んでもカウントが持続します。また、どこで Counter.make しても構いません。
カウンターが引数を取ると、step がその値に変わります。なので、カウントを進めずに値だけ知りたいときは、引数を inc(0) のようにすれば可能です。引数の効果はその場限りで、あとは最初に与えられた step が使われます。
class Nya def hoge puts count end end Counter.make(:count) puts count #=>0 Nya.new.hoge #=>1 puts count(6) #=>7 puts count #=>8
ちょっと注意しておくと、初期値のデフォルトは 0 なので、例えば10回カウントしてカウンターは 9 になりますが、最後に値を取り出すときにもう一度呼べばカウンターは 10 を返すことになります。あるいは初期値を 1 にして 10回カウントし、引数 0 で値を取り出してもいいです。
Counter.make(:count) 10.times {count} puts count #=>10 Counter.make(:count, 1) 10.times {count} puts count(0) #=>10
コードは以下。
class Counter def self.make(mname, counter = 0, step = 1) f = true counter -= step Object.class_eval do define_method(mname) do |*arg| if arg.size >= 1 counter += step if f counter += arg[0] else counter += step end f = false counter end end end end