呼び出す度に数値が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

ほぅ。カウントダウンしている…。

それで、名前はincdecじゃなくてもいいようなんですよ。

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
ruby_pack8

100円〜で好評発売中!
M'ELBORNE BOOKS