2008-09-25 256 views
5

這些for循環是算法形式正確性證明的第一個基本例子。他們有不同的但等效的終止條件:在後置循環終止條件

1 for (int i = 0; i != N; ++i) 

2 for (int i = 0; i < N; ++i) 

的區別變得清晰:

  • 第一個給出了i == N循環結束後的有力保障。

  • 第二個只給出了在循環終止後i >= N的弱保證,但您會被假設爲i == N

如果出於任何原因增量++i是不斷變化的東西像i += 2,或者如果i被修改的循環中,或者如果N爲負,該程序可能會失敗:

  • 的第一個可能陷入無限循環。它在出現錯誤的循環中提前失敗。調試很容易。

  • 第二個循環將終止,並且由於您對i == N的錯誤假設,程序可能會稍後失敗。它可能會遠離導致錯誤的循環,使其很難追溯。或者它可以默默繼續做出意想不到的事情,這更糟糕。

你更喜歡哪種終止條件,爲什麼?還有其他的考慮嗎?爲什麼許多知道這個的程序員拒絕應用它?

+1

對於那些提到我不在for之外的人,有必要指出,在推理程序時我可以使用終止時的i值。例如,如果你想用一個保持不變P(i)的循環來建立P(N),那麼i == N給你P(N),而當i> = N時不會。 – mweerden 2008-09-25 13:03:30

+0

+1表達良好的問題,清楚地表達每個選項的優點。 – 2010-10-06 08:23:29

回答

2

我們不應該孤立地看待計數器 - 如果由於某種原因某人改變了計數器增量的方式,他們會改變終止條件和結果邏輯(如果它是i == N所需的)。

我寧願第二個條件,因爲它更標準,不會導致無限循環。

1

如果你信任你的代碼,你也可以做。如果你想讓你的代碼可讀性和容易理解(並且因此更容忍改變某個你認爲是klutz的人),我會使用類似於;

for (int i = 0 ; i >= 0 && i < N ; ++i) 
1

我總是用#2,那麼你可以肯定的循環將終止...依託它是等於N之後是依靠副作用......難道你只是使用更好變量N本身?

對不起...我的意思#2

0

一般來說,我寧願

for (int i = 0; i < N; ++i) 

在生產的童車程序的懲罰,似乎少了很多嚴重的,你不會有一個線程在一個永遠困for循環,這種情況這可能非常危險並且很難診斷。

此外,通常我喜歡避免這些類型的循環,以支持更易讀的foreach樣式循環。

1

我認爲大多數程序員使用第二個,因爲它有助於弄清楚循環內部發生了什麼。我可以看看它,並且「知道」我將從0開始,並且肯定會小於N.

第一個變體沒有這個質量。我可以看看它,而我所知道的是,我將從0開始,它不會等於N.不是很有幫助。

無論您如何終止循環,在循環外部使用循環控制變量都非常謹慎。在你的例子中,你(正確地)聲明我在循環內部,所以它不在循環的範圍之外,並且它的值的問題是沒有意義的...

當然,第二個變體也有它的優點我看到的所有C參考都使用:-)

0

我更喜歡使用#2,只是因爲我試圖不擴展我在for循環之外的含義。如果我正在跟蹤這樣的變量,我會創建一個額外的測試。也許有人會說這是多餘的或無效的,但它讓我想起我的意圖的讀者:在這一點上,我必須等於n,

@timyates - 我同意一個不應該依賴的副作用

4

我傾向於使用第二種形式,只是因爲我可以更確定該循環將終止。即通過改變循環內部的i來引入非終止錯誤更加困難。

當然,它也有被少一個字符鍵入略微懶惰優勢;)

我還認爲的是,在與合理的範圍規則語言,作爲i爲循環結構內聲明,它不應該在循環之外提供。這將減輕任何依賴於我在循環結束時等於N ...

0

我想你說得非常好,兩者之間的區別。我有如下意見,雖然:

  • 這不是「語言無關」,我可以看到你的例子是在C++中,但有 情況下,你不允許修改裏面的循環變量語言 循環以及其他不能保證索引的值在 循環(以及其中一部分都可用)後可用的其他索引。

  • 聲明該fori 指數,所以我不會對i循環之後的價值投注。 這些例子有點誤導,因爲它們暗示for是 確定的循環。在現實中,它僅僅是一個寫作的更方便的方法:

    // version 1 
    { int i = 0; 
        while (i != N) { 
        ... 
        ++i; 
        } 
    } 
    

    注意i是如何塊後不確定。

如果程序員知道所有上述不會使i價值的一般假設,將是明智的選擇i<N作爲結束條件,以確保出口條件將最終實現。

0

在C#中使用上述任何一種會導致編譯器錯誤,如果你用我的外循環

0

我喜歡這有時會:

for (int i = 0; (i <= (n-1)); i++) { ... } 

此版本的顯示值的直接的範圍內,我可以有。如果你真的需要這個,你的代碼有太多的副作用,需要重寫。

另一個版本:

for (int i = 1; (i <= n); i++) { ... } 

可以幫助您確定循環體是如何經常被稱爲。這也有有效的用例。

0

對於一般的編程工作,我更喜歡

for (int i = 0; i < N; ++i) 

for (int i = 0; i != N; ++i) 

因爲它是不太容易出錯,尤其是當代碼被重構。我已經看到這種代碼偶然變成了一個無限循環。

這個論點說「你會被假設爲我== N」,我不相信是真的。我從來沒有做過這樣的假設,也沒有見過其他程序員做出這樣的假設

0

從我的正式驗證和自動終止分析的角度來看,我強烈傾向於#2(<)。跟蹤一些變量是很容易的(在var = x之前,對於某些非負數n,在var = x+n之後)。然而,要想看到i==N最終成立並不那麼容易。爲此,需要推斷在每一步中i確切地增加了1,這在更復雜的示例中可能由於抽象而丟失。

如果你考慮循環增加2(i = i + 2),這個一般的想法變得更容易理解。爲了保證終止,現在需要知道i%2 == N%2,而當使用<作爲條件時,這是不相關的。