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

相關文章
相關標籤/搜索