18

進行審覈時,我有時會遇到這樣一種循環:循環中的最後一個元素是否應該單獨處理?

i = begin 
while (i != end) {  
    // ... do stuff 
    if (i == end-1 (the one-but-last element)) { 
     ... do other stuff 
    } 
    increment i 
} 

然後,我問一個問題:你寫的嗎?

i = begin 
mid = (end - begin)/2 // (the middle element) 
while (i != end) {  
    // ... do stuff 
    if (i > mid) { 
     ... do other stuff 
    } 
    increment i 
} 

在我看來,這個節拍編寫循環的意圖:你循環,因爲有共同的每個元素做一些東西。使用這個構造,對於你做一些不同的元素。所以,我的結論,你需要這些元素單獨的循環:

i = begin 
mid = (end - begin)/2 //(the middle element) 
while (i != mid) {  
    // ... do stuff 
    increment i 
} 

while (i != end) { 
    // ... do stuff 
    // ... do other stuff 
    increment i 
} 

現在我甚至對等如何寫在一個不錯的方式if -clause看到question ...而我難過:什麼不在這裏。

我錯了嗎?如果是這樣的話,那麼在編碼的時候有什麼特別的情況讓您瞭解到循環體的特點?

+0

的情況下,這一切都是巨大的。我可以改善我的個人工作風格。 Thx – 2014-07-10 18:13:46

+0

「你循環,因爲每個元素都有一些共同點。」這取決於這個「東西」是多麼普遍。所有元素都是汽車可能很常見。有些是藍色的,有些則是紅色的。如果藍色,然後選擇藍色,如果紅色,然後選擇紅色的顏色。所以在某一點上,循環中的決定可能是有道理的。但在某些時候,你應該明白你應該分裂循環。 – 2014-07-11 09:33:25

+0

@peter_the_oak:我同意你根據你在循環中遇到的值做出決定,但不是基於索引。 – xtofl 2014-07-11 14:07:36

回答

5

@xtofl,

我同意你的問題。

我遇到過百萬次類似的問題。

任一開發人員都會爲第一個元素或最後一個元素添加特殊處理。

在大多數情況下這是值得從startIdx + 1只是環或endIdx - 1個元件或者甚至一個長循環分成多個較短的環路。

在極少數情況下,無法分割循環。

在我看來,不常見只要有可能,應該在循環之外處理事情。

1

當然,可以拉出的特殊外殼的東西是愚蠢的。但我不會重複do_stuff;我要麼把它放在一個函數或宏中,所以我不復制粘貼代碼。

1

哪一個表現更好?

如果項目數量非常大,那麼我總是會循環一次,特別是如果您要對每個項目執行某些操作。評估條件的成本很可能低於循環兩次。

糟糕,當然你不會循環兩次......在這種情況下,最好使用兩個循環。不過,我認爲首要的考慮應該是績效。如果您可以通過簡單操作循環邊界(一次)對工作進行分區,則無需在循環中引發條件(N次)。

10

我知道我已經看到了這個,當人們試圖加入一個數組的元素融入到一個逗號分隔字符串:

for(i=0;i<elements.size;i++) { 
    if (i>0) { 
    string += ',' 
    } 
    string += elements[i] 
} 

你要麼有,如果從句中有,或者您有複製的字符串+最後一行再次行。

在這種情況下,顯而易見的解決方案是

string = elements.join(',') 

但join方法做內部同一迴路。並不總是有一種方法來做你想做的事。

5

我開始意識到,當我在for循環中加入特殊情況時,我通常對自己的好處太聰明。

2

我認爲你是正確的循環意味着平等地處理所有元素。不幸的是,有些時候有些特殊情況,這些應該通過if語句在循環結構中處理。

如果有很多特殊情況,儘管您應該考慮想出一些方法來處理單獨構造中的兩組不同的元素。

1

我不願看到的另一件事是for-case pattern

for (i=0; i<5; i++) 
{ 
    switch(i) 
    { 
    case 0: 
     // something 
     break; 
    case 1: 
     // something else 
     break; 
    // etc... 
    } 
} 

我在真正的代碼,看到了這一點。

19

我不認爲這個問題應該由一個原則來回答(例如「在循環中,平等地對待每個元素」)。相反,您可以查看兩個因素來評估實施是好還是壞:

  1. 運行時有效性 - 編譯後的代碼運行速度快,還是以更快的速度運行?
  2. 代碼可維護性 - 對於另一個開發人員來說,理解這裏發生的事情很簡單嗎?

如果速度更快,並且通過在一個循環中完成所有操作,代碼更具可讀性,那就這樣做。如果速度較慢,可讀性較差,則以另一種方式進行。

如果它更快,不易讀,或更慢但更易讀,請找出哪些因素在您的特定情況下更重要,然後決定如何循環(或不循環)。

5

在你發佈的最後一個代碼片段中,你正在重複代碼// ....做東西。

當你在一組不同的索引上有完全不同的操作時,保持2個循環是有意義的。

i = begin 
mid = (end - begin)/2 //(the middle element) 
while (i != mid) {  
    // ... do stuff 
    increment i 
} 

while (i != end) { 
    // ... do other stuff 
    increment i 
} 

這不是這種情況,你仍然希望保持一個循環。但事實仍然是,你仍然保存(結束 - 開始)/ 2次比較。所以它歸結爲你想要你的代碼看起來整潔還是想節省一些CPU週期。電話是你的。

0

如果僅執行一次,則應在循環外進行特殊情況。

但是,由於範圍限制,可能有一個索引或一些其他變量更容易保留在循環內部。將循環控制結構內的數據結構的所有操作放在一起也可能有一個背景原因,儘管我認爲這是一個單獨的弱論證。

0

它只是根據需要和方便使用它。沒有提到平等對待要素,並且肯定沒有傷害俱樂部語言提供的功能。

3

我想你已經完全釘了。大多數人陷入了循環中包含條件分支的陷阱,當他們可以在外面完成時:這只是更快

例如:

if(items == null) 
    return null; 

StringBuilder result = new StringBuilder(); 
if(items.Length != 0) 
{ 
    result.Append(items[0]); // Special case outside loop. 
    for(int i = 1; i < items.Length; i++) // Note: we start at element one. 
    { 
     result.Append(";"); 
     result.Append(items[i]); 
    } 
} 
return result.ToString(); 

你所描述的情況中只是普通的討厭。想象一下,如果代碼增長並需要重構成不同的方法。

除非你解析XML <grin>循環應儘可能簡單和簡潔。

2

我喜歡簡單,從循環 排除元素,給循環

對於如外spearate治療:讓我們考慮的EOF

i = begin 
while (i != end -1) {  
    // ... do stuff for element from begn to second last element 
    increment i 
} 

if(given_array(end -1) != ''){ 
    // do stuff for the EOF element in the array 
} 
相關問題