カテゴリー:
Ruby
タグ:
 FizzBuzz オブジェクト指向

このエントリーをはてなブックマークに追加
更新日時:
2014年11月15日(土)
作成日時:
2014年11月15日(土)

前の記事 / 次の記事

電車の中でなんとなくふぃずばずーって思い出したら
そういえばオブジェクト指向的に書かれたFizzBuzzって
見たことないなーと思ったので書いてみた。Rubyで。

class Integer
  def fizz?
    "Fizz" if ( self % 3 ).zero?
  end

  def buzz?
    "Buzz" if ( self % 5 ).zero?
  end

  def fizzbuzz?
    "FizzBuzz" if ( fizz? && buzz? )
  end

  def to_fizz
    fizz? || self
  end

  def to_buzz
    buzz? || self
  end

  def to_fizzbuzz
    fizzbuzz? || fizz? || buzz? || self
  end
end

オープンクラスを使ってIntegerクラスを拡張。
ついでに3の倍数の時だけ "Fizz" の to_fizz メソッドと、
5の倍数の時だけ "Buzz" の to_buzz メソッドもつくってみた。

で、

1.upto(100) { |n| puts n.to_fizzbuzz }

 
タイトルの内容はこれで終わりなんだけど、
ググってみたらなかなか興味深かったのでメモ。

どうしてプログラマに・・・プログラムが書けないのか?
FizzBuzz問題を使って社内プログラミングコンテストを開催してみた

そもそもFizzBuzz問題が生まれた背景と実際やってみた例。

書けないっていうのは実際に業務では必要ないってことだと思うんだよね。
普段の業務にプログラミング能力は特に必要無いというか、
具体的にはコピー&ペーストと写経ができれば成り立つっていうことなんじゃないかと。

で、世の中にはそういう仕事が実は多くて、
結果応募者もそういう人材が多いってことなのかなと。

オブジェクト指向 と FizzBuzz
[OOPL] Squeak Smalltalk で、カスケードと thisContext を使って FizzBuzz

やはり先達はいたようで。
SmallTalkよく分かんないけど、

(1 to: 3*5) collect: [:n | n fizz; buzz]

っていうコードを見て、Rubyで

1.upto(3*5) { |n| puts n.fizz.buzz }

と書けるようにしてみたいと思ったので実装してみた。
まずモジュールをつくって

module FizzBuzzString
  def fizz?
    fizzbuzzize "Fizz" if ( num % 3 ).zero?
  end

  def buzz?
    fizzbuzzize "Buzz" if ( num % 5 ).zero?
  end

  def fizz
    fizz? || self
  end

  def buzz
    buzz? || self
  end

  private

    def num
      @num || self
    end

    def fizzbuzzize(str)
      str.prepend( is_a?(String) ? self : "" ).instance_variable_set("@num", num)
      str
    end

end

これをIntegerクラスとStringクラスでincludeする。

class Integer
  include FizzBuzzString
end
class String
  include FizzBuzzString
end

基本的には最初のコードと同じで、
後はfizzbuzzizeっていうキモいメソッドで文字列でも裏で数値を持たせて、
文字列でも数値としての判定ができるようにしている。

ちなみにfizzbuzzではなくて、buzzfizzでも出力できる。

5の倍数の時 "Buzz"、3の倍数の時 "Fizz"、5と3の倍数の時 "BuzzFizz"。

1.upto(100) { |n| n.buzz.fizz } 

メソッドチェインでチェインした分だけ伸ばせる。
3の倍数の時 "FizzFizz"、5の倍数の時 "BuzzBuzz"、3と5の倍数の時 "BuzzFizzFizzBuzz"。

1.upto(100) { |n| n.buzz.fizz.fizz.buzz }

元ネタと同様に7の倍数の時は"Pezz"、

def pezz?
  fizzbuzzize "Pezz" if ( num % 7 ).zero?
end
def pezz
  pezz? || self
end

とか追加しても

1..upto(3*5*7) { |n| n.fizz.buzz.pezz }

と書ける。
折角なのでメタプログラミングしてみる。

module FizzBuzzString
  { fizz: 3, buzz: 5, pezz: 7 }.each do |label, multiple_of|
    define_method "#{label}?" do
      fizzbuzzize "#{label.capitalize}" if ( num % multiple_of ).zero?
    end

    define_method "#{label}" do
      send("#{label}?") || self
    end
  end

  private

    def num
      @num || self
    end

    def fizzbuzzize(str)
      str.prepend( is_a?(String) ? self : "" ).extend FizzBuzzString
      str.instance_variable_set("@num", num)
      str
    end

end

class Integer
  include FizzBuzzString
end

Integerクラスで一度includeすれば済むように、
Stringクラスへのincludeは"Fizz"や"Buzz"の特異メソッドとしてextendするように変更。

これで定義を増やせばあとはコードが自動的に生成される。

{ fizz: 3, buzz: 5, pezz: 7, qazz: 11 }.each do {} の時
1.upto(3*5*7*11) { |n| puts n.fizz.buzz.pezz.qazz } と書ける。

{ fizz: 3, buzz: 5, pezz: 7, qazz: 11, tozz: 13 }.each do {} の時
1.upto(3*5*7*11*13) { |n| puts n.fizz.buzz.pezz.qazz.tozz } と書ける。

Ruby FizzBuzz最短コードメモ (1..100, 51bytes) ネタバレ注意

n ** 4 % -15 とかちょっとよく分からない(笑)
超絶過ぎて笑うレベル。解説見てもへー、だし。
つか負の剰余使ってるの初めて見たし、今後もまた見ないだろう・・・。

でもこういう文字列をインデックスで指定して取り出すと
それが欲しい値になってるみたいなやり方は好き。

最終鬼畜FizzBuzz大全

FizzBuzzの色んなパターン。

Search · fizzbuzz · GitHub

世界中からアップロードされたFizzBuzz。