LoginSignup
10
11

More than 5 years have passed since last update.

C#:FizzBuzz問題を一般化して考えてみた

Posted at

FizzBuzz問題が話題になってから、すでに10年以上経過するわけですが、 C#でFizzBuzz問題について考えてみたいと思います。

FizzBuzz問題とは

まずは、FizzBizz問題とは、以下のような問題です。

1から100までの数をプリントするプログラムを書け。ただし3の倍数のときは数の代わりに「Fizz」と、5の倍数のときは「Buzz」とプリントし、3と5両方の倍数の場合には「FizzBuzz」とプリントすること。

どうしてプログラマに・・・プログラムが書けないのか?から引用

普通に解く(1)

using System;

namespace FizzBuzzApp {
    class Program {
        static void Main(string[] args) {
            for (int i = 1; i <= 100; i++) {
                Console.WriteLine(FizzBuzz(i));
            }
        }

        static string FizzBuzz(int n) {
            if (n % 3 == 0 && n % 5 == 0)
                return "FizzBuzz";
            if (n % 3 == 0)
                return "Fizz";
            if (n % 5 == 0)
                return "Buzz";
            return n.ToString();
        }
    }
}

ロジックの中に、出力のコード(Console.WriteLine)が入るのは嫌なので、FizzBuzzというメソッドを定義しました。

普通に解く(2)

上のFizzBuzzメソッドは、次のようにも書けますね。

static string FizzBuzz(int n) {
    string s = "";
    if (n % 3 == 0)
        s += "Fizz";
    if (n % 5 == 0)
        s += "Buzz";
    return s == "" ? n.ToString() : s;
}

このほうが、比較する回数が少なくなります。
ここまでは、ありふれたFizzBuzz問題のコード。

一般化を試みる

さて、このFizzBuzz問題を一般化してみようと思います。やりたいことは、ルールをFizzBuzzメソッドの外側から与えられるようにしたいということ。 そうすれば、

  • 1からnまでの数をプリントするプログラムを書け。ただし
  • 3の倍数のときは数の代わりに「Jazz」とプリント
  • 5の倍数のときは「Buzz」とプリント
  • 7の倍数のときは「Pizz」とプリント
  • 3と5両方の倍数の場合には「JazzBuzz」とプリント
  • 3と7両方の倍数の場合には「JazzPizz」とプリント
  • 5と7両方の倍数の場合には「BuzzPizz」とプリント
  • 3と5と7の倍数の場合には「JazzBuzzPizz」とプリントすること。

のような問題にも答えることができるようになります。

C#のコードは以下の通り。いままではメソッドでしたが、FizzBuzzクラスとしました。

using System;
using System.Collections.Generic;
using System.Linq;

namespace Sample {
    class Program {
        static void Main(string[] args) {
            var fb = new FizzBuzz();
            fb.AddRule(n => n % 3 == 0, _ => "Fizz");
            fb.AddRule(n => n % 5 == 0, _ => "Buzz");
            fb.Execute(1, 100, Console.WriteLine);
        }
    }

    public class FizzBuzz {
        class Rule {
            public Func<int, bool> Condition { get; set; }
            public Func<int, string> Value { get; set; }
        }

        private List<Rule> rules = new List<Rule>();

        public void Execute(int start, int stop, Action<string> action) {
            string GetText(int n) {
                var s = rules.Aggregate("", (a, rule) => rule.Condition(n) ? a + rule.Value(n) : a);
                return (s == "") ? n.ToString() : s;
            }       

            for (int i = start; i <= stop; i++) {
                action(GetText(i));
            }
        }

        public void AddRule(Func<int, bool> cond, Func<int, string> value) {
            rules.Add(new Rule { Condition = cond, Value = value });
        }
    }
}

AddRuleメソッドで、ルールを追加しています。 第1引数が条件のラムダ式、第2引数が出力する文字列を組み立てるラムダ式です。 第2引数は、単純な文字列でも良かったのですが、柔軟性を持たせるためにラムダ式を受け取るようにしました。

Executeでは、ローカル関数を使ってみました。あまり意味ないけど...

単純なバージョンと比べてかなり複雑になってしまいましたが、このFizzBuzzクラスを利用すれば、以下のようにルールを変更することができます。

    static void Main(string[] args) {
        var fb = new FizzBuzz();
        fb.AddRule(n => n % 3 == 0, _ => "Jazz");
        fb.AddRule(n => n % 5 == 0, _ => "Buzz");
        fb.AddRule(n => n % 7 == 0, _ => "Pizz");
        fb.Execute(1, 200, Console.WriteLine);
    }

この記事は、Gushwell's C# Programming Pageで公開したものを加筆・修正したものです。

10
11
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11