RubyでFizzBuzz書いてみた

  • 0
    Like
  • 2
    Comment

    RubyでFizzBuzz書いてみます

    FizzBuzzとは?

    • 1 から順に数を出力
    • 3 で割り切れるならばFizzと出力
    • 5 で割り切れるならばBuzzと出力
    • 3 でも 5 でも割り切れる場合はFizzBuzzと出力

    といった感じの要件を満たすプログラムです。
    僕も新卒のOJTの際に書いてみてって言われてJava,JavaScriptで書いた覚えがあります。

    現在仕事でRubyを使っているので、初心にもどって書いてみます。
    まずはよく見かける形でコーディング

    fizz_buzz.rb
    (1..100).each do |i|
        if i % 15 == 0
            puts "FizzBuzz"
        elsif i % 3 == 0
            puts "Fizz"
        elsif i % 5 == 0 
            puts "Buzz"
        else
            puts i
        end
    end
    

    ふむ
    まずこの(1..100)というRangeをターミナルから入力して自由に決めたい。
    あとは、ちょっとずつメソッドに切り出してみよう。
    加えてRubyのメソッドを存分に使う形で組み替えてみます。

    ということで、うねうねした結果がこちら。

    fizz_buzz.rb
    def console(args)
      raise "argument is nil" if args.nil?
      puts map_upto args.to_i
    end
    
    def map_upto(max)
      (1..max).map { |n| fizzbuzz n }
    end
    
    def fizzbuzz_hash
      { 'fizz' => 3, 'buzz' => 5 }.sort_by { |_, v| v }.to_h
    end
    
    def fizzbuzz(n)
      fizzbuzz_hash
        .map { |str, val| str if (n % val).zero? }.join
        .tap { |str| break str.empty? ? n : str }
    end
    
    if __FILE__ == $0
      console ARGV[0]
    end
    

    初めとぜんぜん違う感じになりました。
    一つずつ見ていきます。

    fizz_buzz.rb
    def console(args)
      raise "argument is nil" if args.nil?
      puts map_upto args.to_i
    end
    

    ターミナル上に表示させるためのメソッド。
    consoleというメソッドの引数にターミナル上での入力を渡す予定です。
    このスクリプトを実行した際に、引数がなければ例外をあげます。
    to_i で引数をStringからIntegerへ変更しmap_uptoメソッドへ渡しています。
    ※引数が文字列であった場合でもto_iすることで0となります。

    続いてこちら

    fizz_buzz.rb
    def map_upto(max)
      (1..max).map { |n| fizzbuzz n }
    end
    

    maxが先程map_uptoメソッドへ渡された値となります。
    もとのコードで(1..100)となっている部分をmaxで置き換えた感じです。
    1から順番に渡された引数までをnという値で順次fizzbuzzメソッドへ渡しています。
    ここの戻り値がコンソール表示される値となります。

    続いてこちら

    fizz_buzz.rb
    def fizzbuzz_hash
      { 'fizz' => 3, 'buzz' => 5 }.sort_by { |_, v| v }.to_h
    end
    

    こちらは単に固定値のHashを返すだけのメソッドとなります。
    ※ @QUANON さんにアドバイス頂き修正しました。Hashの中身の数値でsortすることで
    中身の順番に依存しなくなります。

    続いてこちら

    fizz_buzz.rb
    def fizzbuzz(n)
      fizzbuzz_hash
        .map { |str, val| str if (n % val).zero? }.join
        .tap { |str| break str.empty? ? n : str }
    end
    

    まずfizzbuzz_hashが先程のHashとなります。
    mapの部分で、引数で受け取ったnが3,5で余りなく割り切れるかどうかでstr、つまりHashのキーの部分を返しています。

    mapは、表示する値を配列にして返すので、例えばnが15の場合、['fizz','buzz']という配列を返すことになります。

    対して、余りが生じるような場合は、空の配列を返すことになります。
    この配列をjoinすることで文字列として返すことが出来ます。
    最後にtapを使うことで、文字列が空の場合は引数を、そうでない場合は文字列を返しています。

    でもって最後

    fizz_buzz.rb
    if __FILE__ == $0
      console ARGV[0]
    end
    

    __FILE__ は現在実行しているプログラムのファイル名を表していて
    $0は実行時に指定されたファイル名を相対パスで表しているそうです。
    これらが等しい場合に、consoleメソッドに入力値を表すARGV[0]を渡しています。
    では最後に結果を見てみます。

    ruby fizz_buzz.rb 15
    1
    2
    fizz
    4
    buzz
    fizz
    7
    8
    fizz
    buzz
    11
    fizz
    13
    14
    fizzbuzz
    

    いけてるようですね。
    時間があればテストも追加しようと思います。