2010年11月16日

以TDD開發『數字轉中文』程式

嘗試以TDD(測試導向開發, Test-Driven development)方式進行開發
程式片段與測試可以看這裡
過程大致如下:

  1. 不用多說就先寫出測試函數ConvertNum2CStringTest1,先測ConvertNum2CString(1)吧!(如果為了方便先寫空程式再產生Testing也可)
  2. 再讓ConvertNum2CString()傳入1可以得到"一"
  3. 接下來讓個位數正確
  4. 讓千位數以下有『?千?百?十?』
  5. 考慮中間位數為0補上『零』
  6. 考慮中間位數連續多個0只補一個『零』
  7. 當尾數為0不可補『零』喲
  8. 接下來處理每四位數的『萬、億』
  9. 『萬、億』中間補零問題
  10. 差不多了,試試『一億』『二十億零一』之類的


簡略的心得:
  1. 心態調整:採用TDD會增加工作量?我覺得可以換個方向想,本來程式開發的過程就會不停的進行測試,現在能把每階段的測試過程保留下來,並且自動的重複執行,反而節省了測試成本。
  2. 撰寫過程:TDD的開發過程中,先從最簡單的空函式,再一步一步處理下一個問題...,會迫使你用漸進的思考方式,而不用一開始就考慮整個運算架構(如:怎麼補零,『萬、億』的進位),可以說非常合乎敏捷式開發,沒遇到問題前先用最簡單方式作的精神。
  3. 反覆測試:每當為了處理了更複雜的問題,而對原來的程式進行修改或重構,只要再執行一次測試,就不怕原來對的地方被改錯了。可以說修改時更有信心。
  4. 邊創作邊毀滅:之前看過對測試導向的一個說法,你要在兩個角色之間一直切換:先扮演找碴者,極盡所能想出能讓現有程式出錯的測試;再扮演創造者,解決這個出錯問題。這樣一來,能強迫自己單方面作為程式設計者,對成果可能有的姑息心態。
當然,目前只是抱著玩玩看的心態,寫個小功能。如果是整個系統都要這樣開發,一定會有更多的挑戰。

2010年11月12日

數字轉中文的程式段

為了嘗試TDD(測試導向開發, Test-Driven development)
寫了一個數字轉中文的函式

在網路上意外發現,要用這個題目找個簡短一點的C#程式段好像沒想像中多
雖不敢說自己寫的是最好
但可讀性應該還可以
扣掉註解後程式約30行
提供需要的人參考(不考慮小數部分)

有空再寫以測試導向的思維,進行以下開發的過程
也歡迎先進不吝指點一二


public string ConvertNum2CString(int intNum)
{
    if (intNum == 0) return ("零");

    string[] strPosition = {"", "萬", "億"};
    string strTheValue = "";
    string strReturn = ""; //傳回值
    string strMod = "";

    //從尾巴開始,每次處理四個位數
    for (int i = intNum, j = 0; i >= 1; i = i / 10000, j++)
    {
        strTheValue = ConvertNum2CString1000(i % 10000);
        if (!string.IsNullOrEmpty(strTheValue))
        {
            strReturn = strTheValue + strPosition[j] + strMod + strReturn;
        }
        else if (strReturn.Length > 0 && strReturn.Substring(0, 1) != "零") //本位數雖空白 但後面有數字
        {
            strReturn = "零" + strReturn;
        }

        //如果本四個位數不滿四位且非0 則 上一級尾數需加 "零" 如: 五萬零三
        strMod = (i % 10000 > 0 && i % 10000 < 1000) ? "零" : "";
    }
    return (strReturn);
}

/// <summary>
/// 處理千位以下 數字轉中文
/// </summary>
/// <param name="intNum"></param>
/// <returns></returns>
private string ConvertNum2CString1000(int intNum)
{
    string strCString = "零一二三四五六七八九";
    string[] strPosition = {"", "十", "百", "千" };
    string strReturn = ""; //傳回值
    int intTheValue;
    int intPosition = 0; //目前處理到幾位數

    //從尾巴開始,每次處理一個位數
    for (int i = intNum; i >= 1; i = i / 10)
    {
        intTheValue = i % 10; //目前處理數值

        if (intTheValue > 0)    //目前數字 > 0才需加位數
        {
            strReturn = strCString.Substring(intTheValue, 1) + strPosition[intPosition] + strReturn;
        }
        else if (strReturn.Length > 0 && strReturn.Substring(0, 1) != "零")  //目前數字雖為0但後面有數字,且下一位不是零 則加"零"
        {
            strReturn = "零" + strReturn;
        }

        intPosition++;
    }
    return (strReturn);
}





================================================================


附上測試程式

public void ConvertNum2CStringTest1()
{
    BaseUtility objUtility = new BaseUtility();
    Assert.AreEqual("一", objUtility.ConvertNum2CString(1), "未傳回預期的值。");
    Assert.AreEqual("五", objUtility.ConvertNum2CString(5), "未傳回預期的值。");
    Assert.AreEqual("一十", objUtility.ConvertNum2CString(10), "未傳回預期的值。");
    Assert.AreEqual("八十六", objUtility.ConvertNum2CString(86), "未傳回預期的值。");
    Assert.AreEqual("二百五十", objUtility.ConvertNum2CString(250), "未傳回預期的值。");
    Assert.AreEqual("四千二百七十九", objUtility.ConvertNum2CString(4279), "未傳回預期的值。");
    Assert.AreEqual("零", objUtility.ConvertNum2CString(0), "未傳回預期的值。");
    Assert.AreEqual("三百零五", objUtility.ConvertNum2CString(305), "未傳回預期的值。");
    Assert.AreEqual("三千零五", objUtility.ConvertNum2CString(3005), "未傳回預期的值。");
    Assert.AreEqual("一萬", objUtility.ConvertNum2CString(10000), "未傳回預期的值。");
    Assert.AreEqual("五萬三千零五", objUtility.ConvertNum2CString(53005), "未傳回預期的值。");
    Assert.AreEqual("五千萬三千零五", objUtility.ConvertNum2CString(50003005), "未傳回預期的值。");
    Assert.AreEqual("五千二百萬三千零五", objUtility.ConvertNum2CString(52003005), "未傳回預期的值。");
    Assert.AreEqual("五千二百零九萬三千零七十五", objUtility.ConvertNum2CString(52093075), "未傳回預期的值。");
    Assert.AreEqual("五千二百零九萬", objUtility.ConvertNum2CString(52090000), "未傳回預期的值。");
    Assert.AreEqual("一億", objUtility.ConvertNum2CString(100000000), "未傳回預期的值。");
    Assert.AreEqual("五十萬零三", objUtility.ConvertNum2CString(500003), "未傳回預期的值。");
    Assert.AreEqual("二十億零一", objUtility.ConvertNum2CString(2000000001), "未傳回預期的值。");
    Assert.AreEqual("二十億零三百萬零一", objUtility.ConvertNum2CString(2003000001), "未傳回預期的值。");
}