控制結構
本章重點
◆ 建立和組合邏輯測試
◆ 使用if和switch進行分支處理
◆ 使用while和for
◆ 使用exit和die退出頁面的執行
如果不能讓程式可因不同的情況來決定不同的執行,就很不容易編寫出有用的程式。簡單地說,輸出顯示變數的程式碼行為取決於某個變數的值,做為一個程式設計師,我們可以透過不同的動作讓程式對事件做出不同的回應(可依照外在世界、時間、使用者的輸入或資料庫的內容等等來配合)。
這種程式回應需要一種「控制結構(control structure)」,這個結構可控制指示在不同的情況下應該配合不同的程式碼來執行。在上一章中,我們使用了if這樣的控制結構,但沒有真正深入講解它,在這一章中,我們會介紹PHP提供的每種控制結構,並詳細研究它們的運作與操作方式。
針對有經驗的C語言程式設計師:在PHP的所有功能裡,「控制」這部份是與C語言風格上最相似的,C語言中原來使用的所有結構都可以在這裡使用,而且運作的方式也相同。如果你是有經驗的C語言程式師可以跳過前面直接閱讀本章末尾的小節。
我們將討論的兩大控制結構類型是分支(branch)和迴圈(loop)。分支是程式執行通路上的一個分叉口,取決於某種測詩,程式可選擇向左進行或向右進行,以後的路可能不相同,也可能重新匯合在一起。迴圈是某種分支類型之一,它有一條執行路徑轉回到分支的開始處,可重覆進行測試度可能重覆迴圈執行。
在有效利用控制結構之前,必須能夠有效地建構測試條件。我們先從最簡單的測試開始,先瞭解常數TRUE和FALSE,然後在更複雜的程式碼中使用這些測試。
Boolean運算式
本章中介紹的每種控制結構都含有兩個截然不同的部份:一個是測試部份(決定往哪能個方向進行),一個是由測試的程式碼(為單獨的分支或是迴圈)測試是透過Boolean運算求值進行的,以「真」或「非真」的判斷為運算式的結果。
Boolean常數
最簡單的運算式類型就是個簡單值(simple value),最簡單的Boolean值就是TRUE和FALSE常數,反之亦然。例如,我們可以在if-else敘述的測試部份嵌入它們:
if (TRUE)print(“This will always print”);elseprint(“This will never print”);上面的範例與下面的確敘述的是相同的:if(FALSE)print(“This will never print”);elseprint(“This will always print”);
邏輯運運算元
邏輯運運算元可以組合其它邏輯(又稱Boolean)值來產生新的Boolean值。PHP支援標準的邏輯運算(and、or、not和xor)前兩個還有可替代的版本,如表7-1所示。
7-1 邏輯性運算子號
對於C語言程式設計師來說,一定很熟悉「&&」和「||」運運算元。「!」運運算元通常稱為「NOT」,原因很明顯。
下面的運算式是邏輯運運算元的範例:
(($statement_1 && $statement_2)||
($statement_1 && ! $statement_2)||
(! $statement_1 && $statement_2)||
(! $statement_1 && ! $statement_2)||
這是一種「同義反覆」,是指無論敘述的變數值是什麼,結果都為真。兩個變數的真正值有四種可能的組合,其中每個都由一個「&&」運運算元表示。這四種中必然有一種為真,因為它們是用「||」運運算元連結在一起的。整個運算式必然為真。
下面例子所使用的xor是更巧妙的「同義反覆」示範:
(($statement_1 and $statement_2 and
$statement_3) xor
((! ( $statement_1 and $statement_2)) or
(! ( $statement_1 and $statement_3)) or
(! ( $statement_2 and $statement_3))))
這個運算式的含義是:「給定三個敘述語句,只能以生下面的這兩種情況之一:若非氖的三個敘述都有為真,就會是有一對敘述不為真。」
邏輯運運算元的優先順序
與其它任何一種運運算元相比,有些邏輯運運算元的優先順序更高,但都還可以使用圓括弧來改變優先順序。以優先順序的高低排列邏輯運運算元的順序比其它的低得多,因此指定運運算元(=)比「and」綁得更緊,但比「&&」綁得松一些。
在http://www.php.net/線上手冊中有一份運運算元的優先順序和關聯性的完整列表。
邏輯運運算元的短路
Boolean運運算元有個非常便利順手的特質就是從左至右結合,並能夠設計讓它「短路(short-circuit)」,如果第一個參數確定為真值,則根本不必再繼續計第二個參數。例如在確定兩個數位非常近似比率,但還要避免可能除以0的錯誤。首先進行測試,為確保除數不是0,使用「!=」(不等於的意思)運運算元即可:
if ($denom != 0 && $numer/ $denome>2)
print(“More than twice as much!”);
在$denom為「0」的情況下,無論第二個運算式是真還是假,「&&」運運算元應該傳回「非真」值。因為短路特性,第二個運算式就不被求值了,因此避免錯誤的發生。在$denom不等於「0」的情況下,「&&」運運算元沒有足夠的資訊取得真值的結論,因此對第二個運運算元求值。
到目前為此,我們正式討論過的是TRUE和FALSE常數,以及如何把它們組合起來,形成其它真與非真值。現在讓我們來看看進行實際Boolean運算測試的運運算元。
比較運運算元
表7-2顯示是比較運運算元,可用於數字或字串(請注意「非整數得比較小心」的說明)。
表7-2 比較子號
下面有些例子,是一些變數指定,後面跟隨個永遠為真的複合測試:
$three = 3;$four = 4;$my_pi = 3.14159;If (($three = =$three) and($four ===$four)and($three !=$four)and($three< $four) and($three<=$four) and($four>=$there) and($three<=$three) and($my_pi > $three) and($my_pi <=$four))print(“My faith in mathenmatics is restored! <BR>”);elseprint (“Sure you typed that right?<BR>”):
請注意個很常見的錯誤:把指定運運算元(=)和比較運運算元(==)弄混在一起。 「if($three = $four)..」這敘述很可能意外地把$three和$four設為相當變數(類型),如果$four為真值,則其測試結果將會為真!
運運算元優先順序
雖然過分依賴優先順序則會使閱讀程式碼的人感到困惑,不過注意到比較運運算元比Boolean運運算元的優先順序高還是很好的觀念。請看下面的例子:
If ($small_num> 2 && $small_num <5)
它除了if必要的兩個括弧外,不需要再多加括弧。
字串比較
比較運運算元既可以用於對字串,也可以用於比較數字(請留意「非常數得比較小心」的說明)。我們希望下面的程式輸出顯示出相應的例子:
if ((“Marx”<“Mary”)and(“Mary”<“Marzipan”)){print(“Between Marx and Marzipan in the ”);print(“dictionary, there was Mary .<BR>”);}
非整數得比較小心
雖然比較運運算元可以對數字或字串操作,但需要注意以下兩方面的問題。
首先,對倍精度浮戰火九(或一個整數與倍精度浮點數)進行大於小於的比較通常布言訴是安全的,但對倍精度浮點數進行相等的比較則會很危險,尤其是在對數字計算的結果進行比較時更要注意。這個問題在於「舍入錯誤」,它會使兩個理論上相等的值稍微差了一點點。
其次,雖然比較運運算元可以像對數字一樣的運作方式串進行對比上的操作,但PHP的自動進行型別轉換,因此有時會在把字串可解釋為數字時得出違反常規的結果,例如,下面的程式碼:
$strinhg_1 = “00008”;$string _2 = “007”;$string_3 = “00008-OK”;If($string_2 <$string_1)Print(“$string_2 is less than $string_1 <BR>”);If($string_3< $string_2)Print(“$string_3 is less than $string_2<BR>”);If ($string_1 < $string_3)Print(“$string_1 is less than $string_3<BR>”);
輸出結論為:
007 is less than 00008 //數字比較
00008-OK is less than 007 //字串比對
00008 is less than 00008-OK//字串比對產生矛盾
如果可以的話,PHP會把字串參數化為數字,當比較的兩側都能夠這樣處理時,就會按數字比較,而不是按字母來比較。PHP4設計人員把它當作一種特性,而不是錯誤。要我們的觀點來看,如果在比較時可能會被轉化為數位字串,最好是還是使用strcmp()函式是比較好的作法(請參照第十章)。
三元運運算元
一種非常有用的結構是三元條件運運算元,它扮演了Boolean運運算元和「真值」分支結構之間的角色。它有作用是帶三個運算式,使用第一個運算式的值來決定另外兩個運算式中的哪一個才是所需的,求值後傳回它的值,該運運算元的文法如下:
test-expression ?yes-expression:no-expression
如果test-expression為真,這個運算式的值是yes-expression的結果,否則,就是no-expression的結果。
舉例來看,下面的運算把$max_num指定為$first_num或$second_num,這取決於兩個參數哪一個比較大:
$max_num = $first_num > $second_num ? $first_num:$second_num;
正如我們可以看出的,上面的敘述等於:
if ($first_num >$second_num )
$max_num = $first_num ;
else
$max_num = $second_num;
上述的例子若使用三元運運算元則會更為簡潔。
分支結構
分支的兩種結構主要結構有if和switch.if是最常用的主要分支選擇,通常也是所有人先學習的條件結構。在某些情況下switch也算是一種不錯選取擇,例如當基於單個值需要多個可能的分支時,或者當一系列if敘述非常麻煩時,非常適合使用switch。
if – else敘述
if的文法是:
if(test)
statement-1
或者,還可以帶有可選取擇的else分支:
if(test)
statement-1
else
statement-2
當處理了if敘述後,test運算會被評算求值,結果被解釋為一個Boolean值。如果test不為真並且沒有else子句,則接著執行if結構後面的下一條敘述.
請注意,文法中的「statement」可以是分號結尾的單一敘述,也可以是大括弧圍住的敘述區塊,或者是另一個條件結構(它本身可看成單一敘述)。條件可相互巢狀嵌套任意深度。另外,Boolean運算式可以是真正的Boolean值(即TRUE、FALSE或Boolean運運算元或函式的結果),也可以是能夠解釋為布林值的其它任何型別的值。
關於布林值的完整資訊,請參考第六章。簡單地說,數字「0」、字串「“0”」和空字串「“”」為非真(false),其它所有值為真(true)。
下面的例子是輸出顯示兩數之間的絕對差,其中示範了使用條件的巢狀嵌套,也提及把測試結果解譯為Boolean值:
if ($first - $second)if ($first > $second){$difference = $first - $second;print(“The difference is $difference<BR>”);}else{$difference = $second - $first;print(“The difference is $difference<BR>”);}elseprint(“There is no difference<BR>”);
這段程式碼依靠數字「0」解釋為非真值,如果difference是「0」,則測試失敗,輸出顯示「no difference」訊息,如果有差別,則執行一步的測試(這個例子是故意製作的,因為直接用像「$first != $second」這樣的寫法就與前述例子相同,且更容易理解)。
附加的else
此刻,以前使用過Pascal 的程式設計師可能很警覺到else部分有點怪,也就是說,else子句怎麼知道它屬於哪個if呢?其實規則很簡單,幾乎與Pascal外的其它所有語言中的情況相同。每個else與最接近的來匹配,當然首先要遵循大括弧限制住的範圍。如果要確保if敘述維持獨立、不需要與else匹配,那可以把它括在一對大弧中,如下所示:
if($num % 2 = =0)// $num為偶數?{if ($num > 2)print(“num is not prime<BR>”);elseprint(“num is odd<BR>”);
如果$num是大於2的偶數,則程式碼將印出「num is not prime」,如果$num為奇數,則印出「num is odd」,如果$num恰好為2,則什麼也不顯示。如果省略了大括弧,則else會附加到內層的if,因此如果$num等2,程式碼也會錯誤地印出「num is odd」,如果$num的確是奇數,程式碼則什麼也不列印。
在本章的例子中,我們常使用取餘數運運算元(%),該運運算元在第十二章中講解。對於這些例子,讀者只需要知道「$x % $y」為「0」的意思是「$x能被$y整除」。
Elseif
進行層疊型式的測試是很常見的,如下面的巢狀嵌套if敘述所示:
if ($day == 5)print(“Five golden rings<BR>”);elseif ($day ==4)print(“Four calling birds<BR>”);elseif ($day ==3)print(“Three French hens<BR>”);elseif ($day ==2)print(“Tow turtledoves<BR>”;elseif ($day ==1)print(“A partridge in a pear tree<BR>”);
這種型式很常見,有一個特殊的elseif結構來處理它。我們可以把前面的例子重寫為:
if ($day == 5)print(“Five golden rings<BR>”);elseif($day == 4)print(“Four calling birds<BR>”);elseif($day == 3)print(“Three French hens<BR>”);elseif($day == 2)print(“Two turtledoves<BR>”);elseif($day ==1)print(“A partridge in a pear tree<BR>”);
if、elseif……結構允許只在彼一個分支測試面成功時循序進行測試。從理論上說,這和前面的例子(一個結構帶五個分支,而不是巢狀嵌套的五個兩頭分支結構)在文法上是不同的,但行為是相同的。自己認為哪種好就用哪個吧。
分支和HTML模式
從前面幾章中你已學過可以根據想要自由使用PHP標記在HTML模式和PHP模式間來回進行切換,無論如何都有是相當便利的。如果需要在頁面中包括大區塊的HTML文字,並且沒有動態程式碼或插入的變數,則切換回HTML模式並直接用文字來包含它,這樣就比用print或echo發送它更為簡單和有效。
不太明顯的是,這個策略即使在本身條件結構內部也有效。也就是說,可以使用PHP決定傳送什麼HTML,然後透過臨時切換回HTML模式來「傳送」這些HTML。
舉例來說,下面的程式碼使用print敘述建構了一個完整的HTML網頁(我們假設了一個female()的Boolean函式來測試它)。
<HTML><HEAD><?phpIf(female()){Print(“TITLE>The women-only site</TITLE><BR>”);print(“</HEAD><BODY>”);print(“This site has been specially constructed”);print(“for women only.<BR> No men allowed here!”);}Else{Print(“TITLE>The men-only site</TITLE><BR>”);print(“</HEAD><BODY>”);
print(“This site has been specially constructed”);print(“for men only.<BR> No women allowed here!”);}?></BODY></HTML>我們可以不用所有這些print文法,而且兩部分支內部都使用HTML程式碼,例如:<HTML><HEAD><?phpIf(female()){?><TITLE>The women-only site</TITLE></HEAD><BODY>This site has been specially constructedFor women only.<BR> No men allowed here!<?php}Else{?><TITLE>The men-only site</TITLE><BR></HEAD><BODY>This site has been specially constructedfor men only.<BR> No women allowed here!<?php}?></BODY></HTML>
這個版本就更難閱讀了,但它們唯一的差別是把每個print敘述換成了以PHP右標記(?>)開始,和以PHP左標記(
在本書是例子中,我們盡量避免含有這種類型的條件,因為為樣對於PHP新手有些難以體會。但這不應該成為阻隔讀者學習的理由,這種方式有自己的好處,其中包括執行速成度快(當處於HTML模式中,PHP引擎要做的所有事情就只是傳遞字元,並等待下一個PHP左標記,這樣一定比解析再執行print敘述更快,尤其它們還包含有雙引號的字串時更是如此)。
Switch
對於某種特定的多路分支類型來說,使用switch結構是比較有用的。Switch 不是根據任意邏輯運算式進行分支,而是按照單一運算式的值來選擇不同的路徑。Switch的文法如下,可選擇性的部分置於中包括弧([])中:
switch( expression){case value -1:statement-2statement-2…[break;]case value-2:statement-3statement-4…[break;]…[default:default-statement]}
expression可以是個變數,或者是任何其它類型的運算式,只要它求值結果為一個簡單的值(整數、倍精度浮點數或字串)。該結構的執行方式是對 expression評算求值,然後測試其結果是否和某種情況的值相等。一旦找到一個匹配的值,後續的敘述則按順序執行,直到特殊的敘述「break;」,或者直到switch結構結束(正如我們後面將要看到的break還可以在迴圈結構中止與退出)。特殊的「default」標記可用在結構尾端,如果到它為止仍沒有其它情況匹配,則它就是與運算式「匹配」的那一段。
例如,我們可以重寫if-else的例子,如下所示:
switch($day){case 5:print(“Five golden rings<BR>”);break;case4:print(“Four calling birds<BR>”);break;case 3:print(“Three French hens<BR>”);break;case 2:print(“Tow turtledoves<BR>”);break;default:print(“A partridge in a pear tree<BR>”);}
這個英文歌詞的例子會在2-5於之中輸出顯示相對應的該行,而在其它日期則列印「A partridge in a pear tree」。
Switch 讓人困擾的地方是,一個匹配的case後的所有case都將會執行,直到有break敘述來停止執行。在「partridge」的例子中,break敘述確保了一次只看到歌詞中的一行。如果去掉break敘述,則會看到幾行都被顯示出來。
迴圈
恭喜!你剛剛跨過了從script編製到「真正的程式設計」之間的界限了。到目前為止,我們看到的分支結構還算有用,但使用它們完成的計算還是有限的。另一方面,任何帶有評算測試和無界限迴圈的語言都能夠比任何其它語言完成更好的功能,在電腦科學理論中,這一點是很確定的。在PHP中你也許並不需要編寫一個C語言編譯器,不過請記住這裡沒有什麼天生的語言限制會阻止你這樣做。
以上就是PHP學習寶典-第七章的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!