顯示廣告
隱藏 ✕
※ 本文為 evenfall 轉寄自 ptt.cc 更新時間: 2012-08-29 21:29:08
看板 Soft_Job
作者 qrtt1 (null)
標題 Re: [請益] 很多層迴圈和if 怎麼寫比較好整理
時間 Sat Jul 16 18:56:52 2011



好擔心回文會不會因為不是高手而被拒絕 >"<
不過至少俺有被高手前輩訓練過 (抖)

基本上 if/else 多層是比較困擾的,
因為它實在是不好用『眼睛』輕易的追蹤。
一來這是件傷眼的事,
另外它會考驗寫code到腦袋混沌時的邏輯判斷。

某大濕說過:
好的 code 看完它能開始討論它想達到什麼目的!
不好的 code 看完它只能開始討論 code 在寫些什麼。

寫出來的程式易不易懂與『鋪陳』的方式有關。
而 if/else 邏輯判斷的為了閱讀舒適感而重新安排是可以重構階段再做。
接下來在正式進入 if/else 代換的經驗之前,
實作者可以思考的不同方式是:
是不是有其它的實作方式,能避免這些條件判斷呢?
像使用不用的公式、演算法程式碼的複雜度就會有所改變。
有些情況是較易理解,易實的的公式用程式寫起來很囉囌。

而公式沒那麼好算,但幾個式字就能算出來的,可能程式會變少,但不那麼好懂。

一種是花時間在註解程式(因為解法並不漂亮只好苦命地打字),
一種是花時間說明公式的由來。

要把 if/else 排得好看,
容易處理的情況是裝載這些條件判斷的 scope 不會太大。
這樣就可以使用 early break/return 的方式
也就是版友 ronnywang 建議的部分。

void func1()
{
    if(a)
    {
        if(b)
        {
                if(c)
                {
                        // do something
                }
        }
        else
        {
                // do something
        }
    }
}

能改寫成:
void func1()
{
    if(!a)
        return ;

    if(b)
    {
            if(c)
            {
                 // do something
            }
    }
    else
    {
            // do something
    }
}

能改寫成:
void func1()
{
    if(!a)
                return ;

    if(b && c)
    {
            // do something
    }
    else
    {
            // do something
    }
}

能改寫成:
void func1()
{
    if(!a)
                return ;

    if(b && c)
    {
        // do something
        return ;
    }

    // do something
}

使用 || 也是差不多的:
void func1()
{
    if(a || b)
    {
        if(c)
        {
                        // do something
        }
        else
        {
                // do something
        }
    }
}

加個 ! 讓它有『變號的效果』 => || 變成 &&
void func1()
{
    if(!a && !b)
                return ;

    if(c)
    {
            // do something
    }
    else
    {
            // do something
    }
}

又能改成:
void func1()
{
    if(!a && !b)
                return ;

    if(c)
    {
            // do something
            return ;
    }

    // do something

}

這簡單的代換、變號技巧是曾經帶領過我的前輩交的,
但他強調這構結的變化,只去去掉了 {} 的層數,
本身的語意遠比這結構的等價交換重要許多。
當我們建立一個 function/method 表示我們替程式加了一個功能,
最好他代表著被呼叫一次精確地達成一個目標(有時沒有明確切割語意它會有多個目標)
而要滿足一個特別的目標的基本結構是:

1. 確認是否有能力執行這個任務
2. 執行任務內容
3. 檢查執行後的狀態是否在控制之中

轉成換 fucntion/method 就是
1. check precondition
2. do something
3. check postcondition

因此我們會養成這樣的寫作習慣:

void target()
{
        if(!precondition1)
            return ;
        if(!precondition2)
            return ;
        if(!precondition ...)
            return ;

        // do something
        // do something
        // do something

        if(!postcondition1)
            throw ...
        if(!postcondition2)
            throw ...
            ...

        return ;
}

如果發現,連 do something 內也有許多 condition 得判斷,
這有可能是:
1. problem space 真的他馬的複雜,接受它的。
2. 這個 function/method 承受太多責任了

思考後,若是 case 2 最好考慮重構。
而重構這項活動指的是整理程式碼,讓它變得好維護。
得提醒自己:
不要成為『去 condition』偏執狂。
該用的時候就大贍寫吧。
這並不是一方面要求大家要針對它重構,
又一方面要求大家容忍它的存在。
重構的目標不是去 condition,而是安排 condition 的位置。
我個人的心得是,將程式進入點安排主要流程的部分減少條件判斷,
將判斷埋在細節實作。
這樣一開始讀主流程的原始碼較不易被許多條件混淆了思緒。

而在實作部分,就能利用 condition 平坦化的小技巧,
讓 {} 不要太多層,愛護您的眼,珍惜您的腦袋 :D

--
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 61.231.54.26
asilzheng:把if (b) {if (c)} 改寫成 if (b && c)時好像有點問題1F 07/16 19:05
x000032001:推2F 07/16 19:05
asilzheng:原本b=true,c=false會不做事  但改之後會做b=false的事3F 07/16 19:06
qrtt1:嗯,確實。不過這是單純結構上來換。能不能換要視實際判斷4F 07/16 19:08
qrtt1:感謝 asilzheng 提醒我不夠嚴謹的部分。
TonyQ:我剛看了一輪竟然沒掃到一樓講的 XD 真是看得太偷懶了6F 07/16 19:09
asilzheng:不過還是推一個  這樣會讓程式好讀很多7F 07/16 19:09
qrtt1:我曾寫過你說的情況的 bug XD8F 07/16 19:10
qrtt1:只是不是因為去 {},是渾然天成地寫了出來 (羞)
Davidjcan:推這篇 :d10F 07/16 20:00
viable:我懷疑過VC++編輯器是不是有BUG  一樓你意思是這樣???11F 07/16 20:40
TonyQ:一樓說的是這兩個判斷在那個範例裡並不是等價12F 07/16 20:42
luciferii:我比較好奇的是,是condition直鋪後用很多括號好讀,還13F 07/16 21:28
luciferii:是如這篇所說邏輯變號將行數簡化好讀?
luciferii:我是覺得如果頭腦不是時常保持清醒,後者似乎出錯(特別
luciferii:是logic error)的機率似乎高些?
qrtt1:能在腦袋清醒時寫最好,不過一天八小時裡總有不清醒的時候。17F 07/16 21:32
luciferii:我是覺得八小時保持清醒還可以,但是通常總時間遠遠超過18F 07/16 21:34
luciferii:八小時...XD
qrtt1:我能有4小時是清醒的就謝天謝地了(逃)20F 07/16 21:36
atpx:推一個, 又學到一項觀念了21F 07/16 21:56
ykjiang:給推22F 07/17 02:25
luciferii:其實還有另一個問題,不過可能對大多數程式沒差23F 07/17 02:45
luciferii:就是改寫成 if (b && c) 後,worst case下應該是比
luciferii:if (b) {if (c)} 多花一滴滴時間
luciferii:在某些需求下這個時間累積起來會很顯著
cyr1216:看完好像真的獲得一些東西 謝謝~~27F 07/17 04:37
ldwang:沒繳學費不好意思 XD28F 07/17 11:45
remmurds:我也是用這招 這招在C C++ Java C#都通吃29F 07/17 14:55
ykjiang:我也常用,只是沒系統化整理,憑洞察跟直覺就改成這樣了30F 07/17 16:41
markbex:推31F 07/17 23:02
dsewnr:有看有推32F 07/17 23:43

--
※ 看板: cukebox 文章推薦值: 0 目前人氣: 0 累積人氣: 1087 
分享網址: 複製 已複製
r)回覆 e)編輯 d)刪除 M)收藏 ^x)轉錄 同主題: =)首篇 [)上篇 ])下篇