iT邦幫忙

0

不同判斷的IF,相同的結果,如何減少重複程式碼?

各位先進好:
我正在練習做一個坦克打磚塊(?)的遊戲,如下圖:
https://ithelp.ithome.com.tw/upload/images/20210606/20136318sKvojguYLu.png

問題是,我有多個If判斷,但它們有相同結果,想請問怎麼樣寫可以減少程式碼。

問題範例(condition A,B 是2不同Boolean函數 甲,乙,丙 是帶入的參數):

//外層if
if ( condition A 甲 )
{
    //內層if
    if ( condition B 甲 )
    {
        do the right thing~~~;
    }
}
if ( condition A 乙 )
{
    if ( condition B 乙 )
    {
        do the right thing~~~;
    }
}
if ( condition A 丙 )
{
    if ( condition B 丙 )
    {
        do the right thing~~~;
    }
}

如同上方範例,不同的判斷式做的事都是do the right thing~~~。
因為習慣重複的程式應該可以做刪減,所以想問問有沒有什麼好方法。

我腦海中閃過的是switch,因為它跟我想要的「結果」很像。

switch (condition)
{
case A:
case B:
case C:
    do the right thing~~~;
    break;
}

以這種方式,多個case做相同的事,但我的條件都是Boolean二分,感覺不符合。

認真想到的可能性是if & logical or,因為它確實可以達到我要的條件篩選和結果。

//外層if
if (condition A 甲 || condition A 乙 || condition A 丙)
{
    //內層if
    if (condition B 甲 || condition B 乙 || condition B 丙)
    {
        do the right thing~~~;
    }
}

可是,這樣理論上運算效率會下降,
因為原程式的外層if為True時,就會以同樣條件去判斷內層if。
例如 condition A 丙 為True,
就會判斷 condition B 丙 是否為True。
可是if & logical or的運算方式會變成:
判斷condition A 丙 為True以後,
會再從condition B 甲、condition B 乙...一路判斷到condition B 丙。
多做了幾次沒有意義的運算。

其實好像也不是一定要修正,但大腦一直在對我發出「這樣不夠好」的訊號哈哈。
自己越寫越覺得自己不適合寫程式,越想也越糾結,只好上來求助。

謝謝用心回答的各位,最後我覺得List的解法算是與目的較為相似,其實這個問題本來就有點吹毛求疵,我主要是糾結在有沒有某種「邏輯」可以解決這樣的問題,但我想不出來,所以才上來問,再次感謝。

貼上真實原始碼作為額外參考(應該非必要?)。

//外層if
if ((hitNum = iHitBlock(Vector2f(tank.left, tank.top)))!=-1)
{
    //內層if
    if (bInsideBlock(Vector2f(tank.left, tank.top),     
        sparrBrick[hitNum].getGlobalBounds())) 
    {
        spTank.setPosition(origin);
        return;
    }
}
if ((hitNum = iHitBlock(Vector2f(tank.left+tank.width,tank.top)))!=-1){
    if (bInsideBlock(Vector2f(tank.left+tank.width, tank.top), 
        sparrBrick[hitNum].getGlobalBounds()))
    {
        spTank.setPosition(origin);
        return;
    }
}
if ((hitNum = iHitBlock(Vector2f(tank.left+tank.width,  
                                 tank.top+tank.height)))!=-1)
{
    if (bInsideBlock(Vector2f(tank.left+tank.width, 
                              tank.top+tank.height), 
                     sparrBrick[hitNum].getGlobalBounds())) 
    {
        spTank.setPosition(origin);
        return;
    }
}

原始碼中:

外層if
是否 一個點 有包含在 一個磚塊 內部?
共有 8個外層if 代表方形坦克8個點(左上、上、右上、右、右下、下、左下、左)。

內層if
是否 這個點 與 這個磚塊 的相交程度超出容錯範圍?
(之所以這樣做是因為 坦克 和 磚塊 一樣大,如果坦克想從2個磚塊中間通過,很容易卡住,所以設計它就算超出一點點,壓到方塊也沒關係。)

最後如果超出容錯範圍就讓這次Move無效,返回原點。

1
Todd
iT邦新手 5 級 ‧ 2021-06-06 16:34:15
最佳解答

雖然我上次寫C++應該是五年前的事
但概念上我覺得是差不多的

我認爲你這些判斷式應該是某些動作後的callback會執行的
先從可讀性下手的話

在這個 scope 宣告你需要做判斷的這些數值

Vector2f leftTop =  Vector2f(tank.left, tank.top)
Vector2f top = Vector2f(tank.left+tank.width,tank.top)
// ...其他 

就會變成

if(iHitBlock(leftTop)!=-1){
// ...
}
if(iHitBlock(top)!=-1){
//...
}
//...

感覺所有判別式裡的

if((hitNum = iHitBlock(Vector2f(tank.left, tank.top)))!=-1)

hitNum 感覺可以不用賦值的吧?
雖然第二層if會用到但感覺他本來應該是外面的scope的變數
感覺是直接將function return value 做判斷就好了

if(iHitBlock(leftTop)!=-1)

因為但它經過每次if判斷都會被重新賦值
經由內部更改外部的變數對於維護或可讀性來說
(就是這個function 不是 pure function )
個人認為不太好
那第二層if就可以改成

if (bInsideBlock(leftTop),     
        sparrBrick[iHitBlock(leftTop)].getGlobalBounds())) 

那最後大概會變成

if(iHitBlock(leftTop)!=-1){
    if (bInsideBlock(leftTop),     
        sparrBrick[iHitBlock(leftTop)].getGlobalBounds())){
        //...
        }
}
//...

判斷式簡化的問題
在不清楚資料結構的寫法上
基本就是雙層的if其實就是&& 就可以把ㄧ層if給消掉
但是這是在第一層過但第二層沒過的而且也沒有事情要做的前提下可以這樣寫

如果資料上及行為上跟你提供的原始碼一樣簡單的話
可能將這個八個點作為一個List並用迴圈判斷誰為-1時
就執行bInsideBlock
大概會像是

for(int i = 0; i<positionList.length; i++){
    Vector2f position = positionList[i];
    if( iHitBlock(position) != -1 ){
     if( bInsideBlock(position,     
        sparrBrick[iHitBlock(position)].getGlobalBounds())){
        //...
        }
    }
}
wahati iT邦新手 5 級 ‧ 2021-06-07 23:20:46 檢舉

感謝Todd的回答,
謝謝你還建議了我可讀性以及pure function的問題。
List的解法算是跟我想要的比較相近,謝謝。

1
小魚
iT邦大師 1 級 ‧ 2021-06-06 17:28:55

好懷念的遊戲啊...
我猜你想要的是不是這樣,

if ( condition A 甲 && condition B 甲)
{
    do the right thing~~~;
}
else if ( condition A 乙 && condition B 乙)
{
    do the right thing~~~;
}
else if ( condition A 丙 && condition B 丙)
{
    do the right thing~~~;
}

因為都只有一個if,
就一起判斷就好了,
另外如果滿足第一個if,
後面的else if就不會被執行了,
(如果有可能同時滿足就不要用else if)
但是我也不確定這樣有沒有比較快,
除非你本來速度很慢,
要不然其實感覺不出來,
要不然也可以自己寫抓時間差的部分,
不過時間每次執行也可能會有誤差,
有時候取平均值比較準.

wahati iT邦新手 5 級 ‧ 2021-06-07 22:48:11 檢舉

感謝小魚,這樣確實會精簡很多,
不過這樣還是會有8個if,
都做相同事情(do the right thing~~~)的問題,不是我要的答案。
還是感謝。

小魚 iT邦大師 1 級 ‧ 2021-06-07 23:15:51 檢舉

如果問題出在都做相同的事情,
那就另外寫個function吧,
不過這個跟程式效率關係應該不大,
比較屬於優化的部分.

wahati iT邦新手 5 級 ‧ 2021-06-07 23:30:44 檢舉

對,應該還是要寫function比較好,
只是根據習慣想找看看有沒有可以用邏輯解決的方案。
謝謝。

2
海綿寶寶
iT邦大神 1 級 ‧ 2021-06-06 17:30:26

參考看看,寫成 function

語法沒測過
概念上大概像底下這樣

checkIfOutOfBound(tank.left, tank.top);
checkIfOutOfBound(tank.left + tank.width, tank.top);
checkIfOutOfBound(tank.left + tank.width, tank.top + tank.height);

function checkIfOutOfBound(int pLeft, int pTop) {
	if ((hitNum = iHitBlock(Vector2f(pLeft, pTop)))!=-1) {
		//內層if
		if (bInsideBlock(Vector2f(pLeft, pTop),     
			sparrBrick[hitNum].getGlobalBounds()))  {
			spTank.setPosition(origin);
			return;
		}
	}
}
wahati iT邦新手 5 級 ‧ 2021-06-07 22:56:30 檢舉

哇~原來真的有機會被海綿寶大回答到耶...達成目的(X)
謝謝海綿寶寶,這樣連帶if也精簡了,或許包成函式還是比較好,
只是原本在寫的時候有種
「功能已經分類好了,不想再包函式」的感覺,所以沒這樣做。
還是感謝。

1
I code so I am
iT邦研究生 2 級 ‧ 2021-06-07 10:09:01

使用 command pattern 或 function programming,Java 程式碼可參考:
https://www.geeksforgeeks.org/command-pattern/

Python map 可輕鬆完成以上的事情,但C/C++我就不熟了。

wahati iT邦新手 5 級 ‧ 2021-06-07 23:15:27 檢舉

感謝I code so I am的回答,
去了解了一下command pattern,
看起來會跟開一個function出來很相似,
另外把邏輯處理分開,也減少重複。
不過跟我想要的不同。
還是感謝。

我要發表回答

立即登入回答