ThoughtWorks代码挑战——FizzBuzzWhizz

好久没发表过文章了,今天看到一篇文章 最难面试的IT公司之ThoughtWorks代码挑战——FizzBuzzWhizz游戏(C#解法)html

看到LZ的2B青年代码,实在是惨不忍睹,故写篇文章来探讨下这类问题的通常思考。git

原题:面试

FizzBuzzWhizz
你是一名体育老师,在某次课距离下课还有五分钟时,你决定搞一个游戏。此时有100名学生在上课。游戏的规则是:
ide

1. 你首先说出三个不一样的特殊数,要求必须是个位数,好比三、五、7。this

2. 让全部学生拍成一队,而后按顺序报数。 spa

3. 学生报数时,若是所报数字是第一个特殊数(3)的倍数,那么不能说该数字,而要说Fizz;若是所报数字是第二个特殊数(5)的倍数,那么要说Buzz;若是所报数字是第三个特殊数(7)的倍数,那么要说Whizz。code

4. 学生报数时,若是所报数字同时是两个特殊数的倍数状况下,也要特殊处理,好比第一个特殊数和第二个特殊数的倍数,那么不能说该数字,而是要说FizzBuzz, 以此类推。若是同时是三个特殊数的倍数,那么要说FizzBuzzWhizz。
5. 学生报数时,若是所报数字包含了第一个特殊数,那么也不能说该数字,而是要说相应的单词,好比本例中第一个特殊数是3,那么要报13的同窗应该说Fizz。若是数字中包含了第一个特殊数,那么忽略规则3和规则4,好比要报35的同窗只报Fizz,不报BuzzWhizz。
htm

 

一道看似简单的题目,其实并无那么简单,若是你直接写的话,那估计就是:blog

if, if , else if , if , for, [0] [1] [2]….three

咱们来理解下这道题:

1. 你首先说出三个不一样的特殊数,要求必须是个位数,好比三、五、7。

不一样的三个数,必须是个位数,这些都是验证条件,你注意到了吗?

2. 让全部学生拍成一队,而后按顺序报数。

生成顺序的数字。

3. 学生报数时,若是所报数字是第一个特殊数(3)的倍数,那么不能说该数字,而要说Fizz;若是所报数字是第二个特殊数(5)的倍数,那么要说Buzz;若是所报数字是第三个特殊数(7)的倍数,那么要说Whizz。

规则:若是是某个特殊数的倍数,输出对应的值,不然输出数字。

4. 学生报数时,若是所报数字同时是两个特殊数的倍数状况下,也要特殊处理,好比第一个特殊数和第二个特殊数的倍数,那么不能说该数字,而是要说FizzBuzz, 以此类推。若是同时是三个特殊数的倍数,那么要说FizzBuzzWhizz。

规则:若是是多个特殊数的倍数,输出全部的对应值。

5. 学生报数时,若是所报数字包含了第一个特殊数,那么也不能说该数字,而是要说相应的单词,好比本例中第一个特殊数是3,那么要报13的同窗应该说Fizz。若是数字中包含了第一个特殊数,那么忽略规则3和规则4,好比要报35的同窗只报Fizz,不报BuzzWhizz。

规则:若是包含第一个特殊数字,则只输出第一个特殊数字所对应的值。

 

OK,思考下咱们该怎样作?。。

 

咱们来抽象的理解下题目: “给你输入一堆数字,而后你根据必定的规则进行parse,而后输出parse 的结果。”

因此这道题目想考察的是你如何定义这些规则,如何应用这些规则,该如何parse呢?

 

让咱们看下规则Rule

Rule,有优先级,而后能够对输入进行Parse,而后Parse又须要一个对应的字典。

因此Rule 像这样:

abstract class Rule
    {
        public abstract int Priority { get; }

        public Dictionary<int, string> SpecialDictionary { get; set; }

        public Rule(Dictionary<int, string> specialDictionary)
        {
            this.SpecialDictionary = specialDictionary;
        }

        public bool ParseNum(int num, ref string result)
        {
            if ((SpecialDictionary != null) && (SpecialDictionary.Count > 0))
            {
                return ParseNumCore(num, ref result);
            }
            else
            {
                return false;
            }
        }

        protected abstract bool ParseNumCore(int num, ref string result);
    }

 

接着Rule3: 若是是某个特殊数的倍数,输出对应的值,不然输出数字,输出数字我放到最外层去处理了,固然若是须要也能够写个Rule2.

class Rule3 : Rule
    {
        public Rule3(Dictionary<int, string> specialDictionary)
            : base(specialDictionary)
        {
        }

        public override int Priority
        {
            get { return 3; }
        }

        protected override bool ParseNumCore(int num, ref string result)
        {
            foreach (var special in SpecialDictionary)
            {
                if (num % special.Key == 0)
                {
                    result = special.Value;
                    return true;
                }
            }

            return false;
        }
    }

 

Rule4:若是是多个特殊数的倍数,输出全部的对应值。

class Rule4 : Rule
    {
        public Rule4(Dictionary<int, string> specialDictionary)
            : base(specialDictionary)
        {
        }

        public override int Priority
        {
            get { return 4; }
        }

        protected override bool ParseNumCore(int num, ref string result)
        {
            List<string> matches = new List<string>();

            foreach (var special in SpecialDictionary)
            {
                if (num % special.Key == 0)
                {
                    matches.Add(special.Value);
                }
            }

            if (matches.Count > 1)
            {
                result = string.Join("", matches);
                return true;
            }
            else
            {
                return false;
            }
        }
    }

 

Rule5:若是包含第一个特殊数字,则只输出第一个特殊数字所对应的值。

class Rule5 : Rule
    {
        public Rule5(Dictionary<int, string> specialDictionary)
            : base(specialDictionary)
        {
        }

        public override int Priority
        {
            get { return 5; }
        }

        protected override bool ParseNumCore(int num, ref string result)
        {
            if (SpecialDictionary.Count > 0)
            {
                var firstSpecial = SpecialDictionary.First();
                if (num.ToString().Contains(firstSpecial.Key.ToString()))
                {
                    result = firstSpecial.Value;
                    return true;
                }
            }

            return false;
        }
    }

 

接下来:最重要的就是Parse 逻辑了,想想应该怎样调用这些Rule呢(visitor ?):

foreach (var student in studentNums)
                    {
                        string parseResult = student.ToString();
                        foreach (Rule rule in rules)
                        {
                            if (rule.ParseNum(student, ref parseResult))
                            {
                                break;
                            }
                        }

                        Console.WriteLine(parseResult);
                    }

 

下面是完整的代码:

private static void FizzBuzz()
        {
            bool isValidInput = false;
            do
            {
                Console.WriteLine("please input three numbers which is units digit, use ',' division ");
                string[] inputNums = Console.ReadLine().Split(',');

                if (ValidSpecialInput(inputNums))
                {
                    isValidInput = true;

                    // create special dictionary to parse the students nums.
                    Dictionary<int, string> special = new Dictionary<int, string>();
                    special.Add(Int32.Parse(inputNums[0]), "Fizz");
                    special.Add(Int32.Parse(inputNums[1]), "Buzz");
                    special.Add(Int32.Parse(inputNums[2]), "Whizz");

                    // get students nums.
                    int studentsCount = 100;
                    var studentNums = Enumerable.Range(1, studentsCount);

                    // create rules to parse.
                    var rules = new List<Rule>() 
                    { 
                        new Rule5(special),
                        new Rule4(special), 
                        new Rule3(special), 
                    }.OrderByDescending(r => r.Priority);

                    // parse logic.
                    foreach (var student in studentNums)
                    {
                        string parseResult = student.ToString();
                        foreach (Rule rule in rules)
                        {
                            if (rule.ParseNum(student, ref parseResult))
                            {
                                break;
                            }
                        }

                        Console.WriteLine(parseResult);
                    }

                    Console.ReadLine();
                }
                else
                {
                    Console.WriteLine("the input is not valid.");
                }
            }
            while (isValidInput == false);
        }

        private static bool ValidSpecialInput(string[] specialInputs)
        {
            bool result = false;
            if (specialInputs.Length == 3)
            {
                return specialInputs.All(input =>
                    {
                        int num = 0;
                        return Int32.TryParse(input, out num) && (num > 0) && (num < 10);
                    });
            }

            return result;
        }

 

一些后续思考:

1:若是输入的不是三个,而是4个,5个 special, 应该怎么改?

2:若是学生数量不是100个,是1000个?

3:若是有限考虑Rule3,而后是Rule4,Rule5,应该怎么改?

4:若是还有另外一个限制条件:好比若是数字是素数,把对应的值按反序输出,如何处理?

5:若是输入不是数字,而是字符串,应该如何处理?

 

完整源码下载:http://files.cnblogs.com/LoveJenny/FizzBuzz.7z

相关文章
相关标签/搜索