01 April 2014
Rubyでカウンタを作る最新の方法があると聞いて...
呼び出す度に数値が1づつ増えたり減ったりするカウンタを、Rubyで書くとしたらどうやりますか?
こんな感じですか。
class Counter
def initialize(init=1)
@current = init
end
def inc
@current += 1
end
def dec
@current -= 1
end
end
c = Counter.new
c.inc # => 2
c.inc # => 3
c.inc # => 4
c.inc # => 5
c.dec # => 4
c.dec # => 3
c.dec # => 2
c.dec # => 1
あるいはEnumeratorを使ったこんな感じですか。
def counter(op=:+, init=1)
Enumerator.new do |y|
cur = init
loop { y << cur; cur = cur.send(op, 1) }
end
end
inc = counter
inc.next # => 1
inc.next # => 2
inc.next # => 3
inc.next # => 4
dec = counter(:-, 10)
dec.next # => 10
dec.next # => 9
dec.next # => 8
dec.next # => 7
それとも、step
とか使いますか。
def counter(n=1, by=1)
n.step(by:by)
end
inc = counter
inc.next # => 1
inc.next # => 2
inc.next # => 3
inc.next # => 4
dec = counter(10, -1)
dec.next # => 10
dec.next # => 9
dec.next # => 8
dec.next # => 7
最新式カウンタ
でも、どうやらそうじゃないらしいんですよ、最新のやり方は。
こうやるらしいんです…。
AprFl.set
def inc
1
end
incメソッドを定義して初期値をセットすればいいらしいのです。
ほんとですかね?
実行してみます。
inc # => 1
inc # => 2
inc # => 3
inc # => 4
なるほど。カウントアップしてる…。
dec
もやってみますね。
AprFl.set
def dec
10
end
dec # => 10
dec # => 9
dec # => 8
dec # => 7
ほぅ。カウントダウンしている…。
それで、名前はinc
やdec
じゃなくてもいいようなんですよ。
AprFl.set
def increment
1
end
increment # => 1
increment # => 2
increment # => 3
increment # => 4
def decrement_for_me
10
end
decrement_for_me # => 10
decrement_for_me # => 9
decrement_for_me # => 8
decrement_for_me # => 7
へぇ。
Counterクラスでもイケるそうなんです。
AprFl.set
class Counter
def initialize(init)
@init = init
end
def inc
@init
end
end
c = Counter.new(3)
c.inc # => 3
c.inc # => 4
c.inc # => 5
c.inc # => 6
引数も取れるらしい。
c.inc # => 7
c.inc # => 8
c.inc(2) # => 10
c.inc # => 11
c.inc(3) # => 14
c.inc # => 15
はぁ。
初期値に数字以外をセットするとどうなりますか?
def inc
"hello, world!"
end
inc # => "hello, world!"
inc # => "hello, world!"
inc # => "hello, world!"
inc # => "hello, world!"
def dec(chr)
chr.next
end
dec('A') # => "B"
dec('A') # => "B"
dec('A') # => "B"
dec('A') # => "B"
普通の挙動ですね。
しかし、世の中知らないことだらけですよ。Rubyにこんなやり方あるなんて!
ということで、Rubyでカウンタを作る最新の方法を紹介しました!
いやいや、AprFl.set
、置いてけって。
module AprFl
def self.set
t = Time.now
define_aprfl_method if [t.mon, t.day] == [4, 1]
end
def self.define_aprfl_method
TracePoint.trace(:return) do |tp|
k, m, v = tp.defined_class, tp.method_id, tp.return_value
op =
case m
when /^inc/ then :+
when /^dec/ then :-
else next
end
k.class_eval {
undef_method m
define_method(m) { |*a, &b| v.send(op, (a.first||1)) rescue v }
}
end
end
end
またTracePointねたですか。
関連記事:
Rubyでワンタイムメソッドを実装して「スパイ大作戦」を敢行せよ!
(追記:2014-4-2) stepの例を追加しました。
blog comments powered by Disqus