电竞比分网-中国电竞赛事及体育赛事平台

分享

Java編程入門(3.3):while與do..while語(yǔ)句

 yy99k 2017-09-14

Java支持簡(jiǎn)單語(yǔ)句和復(fù)合語(yǔ)句。比如賦值和子函數(shù)調(diào)用這樣的簡(jiǎn)單語(yǔ)句是構(gòu)建程序的基礎(chǔ)模塊。像while循環(huán)和if語(yǔ)句這樣的復(fù)合語(yǔ)句用來將簡(jiǎn)單語(yǔ)句組織成復(fù)雜的結(jié)構(gòu)。這種結(jié)構(gòu)被稱作控制結(jié)構(gòu),用來控制語(yǔ)句的執(zhí)行順序。下面的五個(gè)章節(jié)會(huì)討論Java中的控制結(jié)構(gòu),從本節(jié)的while語(yǔ)句和do…while語(yǔ)句開始介紹。與此同時(shí),我們會(huì)給出每種控制結(jié)構(gòu)的示例程序并將其應(yīng)用到前一節(jié)算法設(shè)計(jì)例子中。

3.3.1  while循環(huán)

在之前的3.1節(jié)中已經(jīng)介紹了while循環(huán),結(jié)構(gòu)如下:

1
2
while ( boolean-expression )
   statement

while語(yǔ)句是一個(gè)代碼塊,由一組語(yǔ)句組合在一起并包含在一對(duì)括號(hào)中。結(jié)構(gòu)中的statement被稱作循環(huán)體。當(dāng)boolean-expression為true時(shí),會(huì)循環(huán)執(zhí)行循環(huán)體中的語(yǔ)句。這個(gè)布爾表達(dá)式被稱作循環(huán)條件,執(zhí)行簡(jiǎn)單的測(cè)試。有幾點(diǎn)需要澄清。在循環(huán)體一次都沒有執(zhí)行前,循環(huán)條件為false時(shí)會(huì)發(fā)生什么?這種情況下,循環(huán)體永遠(yuǎn)不會(huì)執(zhí)行。while循環(huán)的主體可能會(huì)任性任意多次,當(dāng)然也可能是0次。當(dāng)執(zhí)行到循環(huán)體中間某條語(yǔ)句時(shí)循環(huán)條件由true變?yōu)閒alse時(shí)會(huì)怎么樣?會(huì)馬上結(jié)束循環(huán)嗎?不會(huì),因?yàn)橛?jì)算機(jī)會(huì)一直執(zhí)行到循環(huán)結(jié)束為止。只有當(dāng)重新跳轉(zhuǎn)到循環(huán)開始并且測(cè)試循環(huán)條件時(shí),循環(huán)才會(huì)結(jié)束。

讓我們來看看使用while循環(huán)解決問題的一個(gè)典型示例:用戶輸入一組正整數(shù),算出它們的平均值。平均值的算法,是所有整數(shù)的和除以整數(shù)個(gè)數(shù)。程序會(huì)要求用戶每次輸入一個(gè)整數(shù),記錄輸入整數(shù)的個(gè)數(shù),然后計(jì)算所有已錄入數(shù)字的和。下面是這段程序的偽代碼:

1
2
3
4
5
6
7
8
Let sum = 0     // 保存用戶輸入所有整數(shù)的和
Let count = 0   // 保存用戶輸入的整數(shù)個(gè)數(shù)
當(dāng)還有整數(shù)要處理時(shí):
    讀入一個(gè)整數(shù)
    加到sum變量
    為count增加計(jì)數(shù)
用sum除以count得到平均值
輸出平均值

如何知道還有待輸入的整數(shù)呢?一種典型的解決方法是,讓用戶在所有數(shù)據(jù)錄入完畢時(shí)輸入0。這種方法在所有數(shù)據(jù)都是正整數(shù)的情況下有效,這時(shí)0不是一個(gè)合法的數(shù)值。0本身不作為計(jì)算平均數(shù)集合中的數(shù)據(jù),僅僅作為真實(shí)數(shù)據(jù)錄入結(jié)束的標(biāo)志。這種方法一些場(chǎng)合下被稱為使用哨兵值。所以,現(xiàn)在while循環(huán)的測(cè)試條件變?yōu)椤爱?dāng)輸入的整數(shù)非0”。但是這里有另外一個(gè)問題!第一次判斷循環(huán)條件時(shí),循環(huán)體尚未執(zhí)行,這時(shí)還沒有讀取任何數(shù)值。也就是說,還沒有“輸入的整數(shù)”。這時(shí)判斷輸入整數(shù)是否為0是沒有意義的。因此,我們需要在循環(huán)執(zhí)行做一些處理,把必要的信息準(zhǔn)備好。這里,我們只要在循環(huán)的前面讀取第一個(gè)整數(shù)就可以了。下面是修改后的算法:

1
2
3
4
5
6
7
8
9
Let sum = 0
Let count = 0
讀取一個(gè)整數(shù)
整數(shù)非0時(shí):
    將整數(shù)加到sum
    增加count計(jì)數(shù)
    讀取一個(gè)整數(shù)
用sum除以count得到平均值
輸出平均值

請(qǐng)注意,這里我重新調(diào)整了循環(huán)體。由于在循環(huán)開始前就讀取了整數(shù),在循環(huán)一開始就需要處理讀到的整數(shù)。在循環(huán)的最后,計(jì)算機(jī)會(huì)讀取一個(gè)新的整數(shù)。計(jì)算機(jī)會(huì)跳轉(zhuǎn)到循環(huán)開始的地方,用新讀取的值測(cè)試循環(huán)條件。注意:當(dāng)計(jì)算機(jī)讀取最后的哨兵值以后,會(huì)直接結(jié)束循環(huán)而不對(duì)其進(jìn)行處理。既不會(huì)累計(jì)到sum中,也不會(huì)算到count里。這就是算法的工作機(jī)制。哨兵值不作為數(shù)據(jù)的一部分。在最初的算法中,不做循環(huán)前的準(zhǔn)備會(huì)累計(jì)所有的值包括哨兵值,所以結(jié)果是錯(cuò)的。(哨兵值為0,因此sum的結(jié)果是正確的,但是count會(huì)多算了1個(gè)。這種常見的錯(cuò)誤被稱作“差一錯(cuò)誤”。循環(huán)中進(jìn)行計(jì)數(shù)會(huì)比看上去難得多?。?/p>

可以很容易地把算法轉(zhuǎn)換成一個(gè)完整的程序。注意,在程序中不能用“average = sum/count;”這樣的語(yǔ)句計(jì)算平均值。因?yàn)閟um和count都是int類型,sum/count的結(jié)果是整數(shù),而平均值應(yīng)該是實(shí)數(shù)。我們之前已經(jīng)遇到過了這個(gè)問題:把其中一個(gè)int值轉(zhuǎn)換為double類型,強(qiáng)制計(jì)算機(jī)把商值作為實(shí)數(shù)計(jì)算。具體的做法,將其中一個(gè)變量強(qiáng)制類型轉(zhuǎn)換為double。“(double)sum”將sum值轉(zhuǎn)為實(shí)數(shù),因此計(jì)算平均數(shù)可以改成“average = ((double)sum) / count;”。還有一種方法,把聲明變量時(shí)就聲明為double類型。

程序還存在另一個(gè)問題:用戶一開始輸入0時(shí),程序不會(huì)再處理其它輸入。這種用例可以來測(cè)試循環(huán)結(jié)束時(shí)count是否還等于0。看起來這是細(xì)節(jié)問題,但是仔細(xì)的程序員應(yīng)該考慮所有的情況。

下面是程序的完整代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
 * 這個(gè)程序會(huì)讀取一個(gè)用戶輸入的正整數(shù)序列,
 * 輸出所有整數(shù)的平均值。
 * 程序會(huì)提示用戶每次輸入一個(gè)整數(shù)。
 * 當(dāng)用戶輸入0時(shí)表示輸入結(jié)束。
 * (0不會(huì)作為數(shù)據(jù)加入平均值計(jì)算)
 * 該程序不會(huì)檢查用戶輸入是否為正整數(shù),
 * 即使輸入負(fù)數(shù)也會(huì)加入計(jì)算。
 */
public class ComputeAverage {
   public static void main(String[] args) {
      int inputNumber;   // 用戶輸入
      int sum;           // 正整數(shù)和
      int count;         // 輸入的正整數(shù)個(gè)數(shù)
      double average;    // 平均數(shù)
      /* 初始化sum和count變量*/
      sum = 0;
      count = 0;
      /* 讀取并處理用戶輸入 */
      System.out.print('Enter your first positive integer: ');
      inputNumber = TextIO.getlnInt();
      while (inputNumber != 0) {
         sum += inputNumber;   // 把inputNumber累加到sum
         count++;              // count加1
         System.out.print('Enter your next positive integer, or 0 to end: ');
         inputNumber = TextIO.getlnInt();
      }
      /* 打印計(jì)算結(jié)果 */
      if (count == 0) {
         System.out.println('You didn't enter any data!');
      }
      else {
         average = ((double)sum) / count;
         System.out.println();
         System.out.println('You entered ' + count + ' positive integers.');
         System.out.printf('Their average is %1.3f.n', average);
      }
   } // end main()
} // end class ComputeAverage

3.3.2  do..while語(yǔ)句

有時(shí)候,在循環(huán)結(jié)尾測(cè)試循環(huán)條件會(huì)比像while循環(huán)那樣開頭判斷更加方便。do..while語(yǔ)句與while循環(huán)非常類似,區(qū)別在于“while”及循環(huán)體被移到了結(jié)尾,在循環(huán)的開頭添加了“do”。do..while語(yǔ)句的結(jié)構(gòu)如下:

1
2
3
do
    statement
while ( boolean-expression );

通常情況下,statement可能是一個(gè)代碼塊:

1
2
3
do {
    statements
} while ( boolean-expression );

注意:在do..while的結(jié)尾有一個(gè)分號(hào)“;”。這個(gè)分號(hào)也是語(yǔ)句的一部分,與賦值語(yǔ)句或聲明語(yǔ)句結(jié)尾的分號(hào)一樣。漏寫分號(hào)會(huì)造成語(yǔ)法錯(cuò)誤。(通常,每個(gè)Java語(yǔ)句都會(huì)以分號(hào)或右花括號(hào)“}”結(jié)尾)

執(zhí)行do循環(huán)時(shí),計(jì)算機(jī)會(huì)首先執(zhí)行循環(huán)體中的語(yǔ)句,然后判斷循環(huán)條件。如果循環(huán)條件表達(dá)式為true,計(jì)算機(jī)會(huì)回到do循環(huán)開頭繼續(xù)執(zhí)行;如果為false,計(jì)算機(jī)會(huì)終止循環(huán)繼續(xù)執(zhí)行程序的其它部分。由于只有在循環(huán)末尾才會(huì)進(jìn)行條件判斷,do循環(huán)至少會(huì)執(zhí)行一次循環(huán)體。

例如,下面的偽代碼是一個(gè)游戲程序。使用do循環(huán)比起while循環(huán)更有意義,至少能玩一盤游戲。程序的開始版本,循環(huán)判斷條件沒有意義:

1
2
3
4
5
do {
   玩一盤游戲
   詢問是否想要再玩一盤
   讀取反饋
} while ( 用戶的反饋是 yes );

把上面的偽代碼轉(zhuǎn)成Java。開始不去討論游戲的細(xì)節(jié),讓我們定義一個(gè)Checkers類,其中包含了一個(gè)static成員函數(shù)叫做playGame(),和程序的使用者玩跳棋游戲。偽代碼“玩一盤游戲”被轉(zhuǎn)換為調(diào)用“Checkers.playGame();”。我們需要一個(gè)變量存儲(chǔ)用戶的反饋。TextIO類通過一個(gè)boolean變量存儲(chǔ) yes/no 的回復(fù)結(jié)果?!癥es”表示true,“no”表示false。因此算法的代碼會(huì)變成:

1
2
3
4
5
6
boolean wantsToContinue;  // True表示用戶想要再玩一盤
do {
   Checkers.playGame();
   System.out.print('Do you want to play again? ');
   wantsToContinue = TextIO.getlnBoolean();
} while (wantsToContinue == true);

當(dāng)boolean變量的值變?yōu)閒alse,表示循環(huán)應(yīng)當(dāng)結(jié)束。在程序的一個(gè)地方賦值,在另一個(gè)地方作為判斷條件——當(dāng)boolean變量這樣使用時(shí),被稱為標(biāo)志(flag)或標(biāo)志變量(表示信號(hào)標(biāo)志)。

順便說一下,程序員通常會(huì)鄙視這樣的寫法“while (wantsToContinue == true)”。這種方式過于教條,可以用“while (wantsToContinue)”代表同樣的含義。類似的,還有“flag == false”這樣的寫法,flag是一個(gè)boolean變量?!癴lag == false”與“!flag”完全等價(jià),這里的感嘆號(hào)!表示對(duì)boolean值進(jìn)行取反操作。所以,可以用“while (!flag)”取代“while (flag == false)”,用“if (!flag)”取代“if (flag == false)”。

盡管do..while語(yǔ)句有時(shí)候比while語(yǔ)句更加方便,但是兩種循環(huán)并沒有讓語(yǔ)言更強(qiáng)大。任何可以用do..while循環(huán)解決的問題都可以用while完成,反之亦然。事實(shí)上doSomething可以代碼任何一個(gè)代碼塊:

1
2
3
do {
    doSomething
} while ( boolean-expression );

與下面的代碼功能一致:

1
2
3
4
doSomething
while ( boolean-expression ) {
    doSomething
}

類似的,

1
2
3
while ( boolean-expression ) {
    doSomething
}

可以替換為:

1
2
3
4
5
if ( boolean-expression ) {
   do {
       doSomething
   } while ( boolean-expression );
}

程序的功能沒有任何變化。

3.3.3  break和continue語(yǔ)句

while循環(huán)與do..while循環(huán)會(huì)在程序的開始或結(jié)尾測(cè)試循環(huán)條件。有時(shí)候,在循環(huán)體中間或者幾個(gè)不同的地方測(cè)試條件會(huì)更加合理。Java提供了在循環(huán)體中跳出循環(huán)的通用方法,叫做break語(yǔ)句,形式如下:

1
break;

當(dāng)計(jì)算機(jī)在循環(huán)體重執(zhí)行break語(yǔ)句時(shí),會(huì)立刻跳出循環(huán)。接下來會(huì)繼續(xù)執(zhí)行循環(huán)后面的語(yǔ)句??紤]下面的示例:

1
2
3
4
5
6
7
8
while (true) {  // 看起來會(huì)一直循環(huán)下去!
   System.out.print('Enter a positive number: ');
   N = TextIO.getlnInt();
   if (N > 0)   // 輸入OK,跳出循環(huán)
      break;
   System.out.println('Your answer must be > 0.');
}
// continue here after break 在break執(zhí)行后,從這里開始繼續(xù)執(zhí)行

如果用戶輸入的數(shù)值大于0,會(huì)執(zhí)行break語(yǔ)句跳出循環(huán)。否則,計(jì)算機(jī)會(huì)輸出“Your answer must be > 0.”然后跳轉(zhuǎn)到循環(huán)的開頭繼續(xù)讀取其它用戶輸入的值。

循環(huán)的第一行,“while (true)”可能會(huì)有一點(diǎn)奇怪,但確是合法的。while循環(huán)的條件可以是任意boolean類型的表達(dá)式。計(jì)算機(jī)會(huì)判斷檢查式的值看是true還是false。boolean值“true”也是一個(gè)boolean表達(dá)式,值為true。所以,“while (true)”表示無限循環(huán),可以通過break語(yǔ)句終止無限循環(huán)。

break語(yǔ)句會(huì)立刻終止包含了該語(yǔ)句的循環(huán)。Java支持循環(huán)嵌套,即一個(gè)循環(huán)中包含另一個(gè)循環(huán)。如果在嵌套的循環(huán)內(nèi)調(diào)用break語(yǔ)句,只會(huì)跳出該層循環(huán),而非跳出外層循環(huán)。還有一種跳轉(zhuǎn)叫做標(biāo)簽中斷(labeled break),可以指定希望跳出的循環(huán)。這種用法并不常見,這里我會(huì)快速帶過。標(biāo)簽(Label)的工作方式如下:可以在任何循環(huán)前面加上標(biāo)簽。標(biāo)簽由一個(gè)簡(jiǎn)單標(biāo)識(shí)符帶一個(gè)冒號(hào)組成。例如,帶label的while循環(huán)看起來像這樣“mainloop: while…”,在循環(huán)內(nèi)部,你可以使用帶標(biāo)簽的跳轉(zhuǎn)語(yǔ)句,比如“break mainloop;”來跳轉(zhuǎn)帶標(biāo)簽的循環(huán)。例如,下面這段代碼檢查兩個(gè)字符串,s1和s2,有一個(gè)共同的字符。如果找到共同字符,標(biāo)志變量nothingInCommon會(huì)置為false,通過標(biāo)簽中斷結(jié)束處理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
boolean nothingInCommon;
nothingInCommon = true// 假設(shè)s1和s2沒有共同字符
int i,j;  // 在s1和s2中用來循環(huán)的迭代變量
i = 0;
bigloop: while (i < s1.length())="">
   j = 0;
   while (j < s2.length())="">
      if (s1.charAt(i) == s2.charAt(j)) { // s1和s2有共同的字符
          nothingInCommon = false;
          break bigloop;  // 跳出所有2層循環(huán)
      }
      j++;  // 繼續(xù)處理s2中的下一個(gè)字符
   }
   i++;  // 繼續(xù)處理s1中的下一個(gè)字符
}

continue語(yǔ)句與break類似,但是很少使用。continue語(yǔ)句告訴計(jì)算機(jī)跳過本次循環(huán)剩余語(yǔ)句的執(zhí)行。然而,與跳出循環(huán)不同,continue會(huì)跳轉(zhuǎn)到循環(huán)開始繼續(xù)下一次迭代(包括判斷循環(huán)變量的值是否需要繼續(xù)迭代)。與break語(yǔ)句類似,在嵌套循環(huán)中執(zhí)行continue語(yǔ)句時(shí),會(huì)直接轉(zhuǎn)到包含該語(yǔ)句的循環(huán)開始;“標(biāo)簽繼續(xù)(labeled continue)“會(huì)轉(zhuǎn)到指定的循環(huán)繼續(xù)執(zhí)行。

break和continue語(yǔ)句可以用在while循環(huán)與do..while循環(huán)中。它們也可以在接下來的章節(jié)中介紹的循環(huán)中使用。在3.6節(jié),我們會(huì)看到在switch語(yǔ)句中使用break。break還可以在if語(yǔ)句中使用,前提是if語(yǔ)句嵌套在循環(huán)火種switch語(yǔ)句中。在這種情況下,break并意味著會(huì)跳出if語(yǔ)句,而是跳出包含著if語(yǔ)句的循環(huán)或switch語(yǔ)句。在if語(yǔ)句中使用continue也是類似的用法。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多