Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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で公開したものを加筆・修正したものです。

gushwell
株式会社ジード / Microsoft MVP for Developer Technologies 2005-2019 / 著書『実戦で役立つ C#プログラミングのイディオム/定石&パターン』『新・標準プログラマーズライブラリ なるほどなっとく C#入門』『C#プログラミング入門―オブジェクト指向のプログラミング手法を基礎から解説』
https://github.com/gushwell
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした