控制結構
本章重點
◆ 建立和組合邏輯測試
◆如果不能讓程式可因不同的情況來決定不同的執行,就很不容易寫出有用的程式。簡單來說,輸出顯示變數的程式碼行為取決於某個變數的值,做為程式設計師,我們可以透過不同的動作讓程式對事件做出不同的回應(可依照外在世界、時間、使用者的輸入或資料庫的內容等來配合)。
這種程式回應需要一種「控制結構(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)||
($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_3) xor
((! ($statement_1 and $statement_2))!orm $statement_1 and $statement_3)) or
(! ( $statement_2 and $statement_3))))
這個運算式的意思是:「給定三個敘述語句,只能以生下面的這兩種情況之一:若非氖的三個敘述都有為真,就會是有一對敘述不為真。順序較高,但都還可以使用圓括弧來改變優先順序。但比「&&」綁得鬆一些。 Boolean運算子有個非常便利順手的特質就是從左到右結合,並且能夠設計讓它「短路(short-circuit)」,如果第一個參數確定為真值,則根本不必再繼續計第二個參數。
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=$there) and($three $three) and($my_pi ”);elseprint (“Sure you typed that right?
”):
請注意個很常見的錯誤:把指定運算子(=)和比較運算子(==)搞混在一起。 「if($three = $four)..」這敘述很可能意外地把$three和$four設為相當變數(類型),如果$four為真值,則其測試結果將會為真!
運算子優先順序
雖然過度依賴優先順序則會使閱讀程式碼的人感到困惑,不過注意到比較運算子比Boolean運算子的優先順序高還是很好的觀念。請看下面的範例:
If ($small_num> 2 && $small_num
它除了if必要的兩個括號外,不需要再多加括號。
字串比較
比較運算子可以用來對字串,也可以用來比較數字(請留意「非常數得比較小心」的說明)。我們希望下面的程式輸出顯示出相應的例子:
if ((“Marx””);}
非整數得比較小心
雖然比較運算子可以對數字或字符串操作,但需要注意以下兩方面的問題。
首先,對倍精度浮戰火九(或一個整數與倍精度浮點數)進行大於小於的比較通常布言訴是安全的,但對倍精度浮點數進行相等的比較則會很危險,尤其是在對數字計算的結果進行比較時更要注意。這個問題在於「舍入錯誤」,它會使兩個理論上相等的值稍微差了一點點。
其次,雖然比較運算子可以像對數字一樣的運作方式串進行對比上的操作,但PHP的自動進行型別轉換,因此有時會在把字符串可解釋為數字時得出違反常規的結果,例如,下面的程式碼:
$strinhg_1 = “00008”;$string _2 = “007”;$string_3 = “00008-OK”;If($string_2 ”);If($string_3”);If ($string_1 ”);
輸出結論為:
007 is less than 00008 //數字比較
00008-OK is less than 007 // 字串比對註 008 //字串比對產生矛盾
如果可以的話,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 的例子使用$3$3使用三元;運算子則會更為簡潔。
分支結構
分支的兩個結構主要結構有if和switch.if是最常用的主要分支選擇,通常也是所有人先學習的條件結構。在某些情況下switch也算是一種不錯選取擇,例如當基於單一值需要多個可能的分支時,或當一系列if敘述非常麻煩時,非常適合使用switch。
if – else敘述
if的語法是:
if(test)
statement-1
或者,還可以帶有可選取擇的else分支:
if(test)
statement-1
else
statement-2
當處理了敘述後,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
”);}else{$difference = $second - $first;print( “ The difference is $difference
”);}elseprint(“There is no difference
”);
這段程式碼依靠數字“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
”);elseprint(“num is odd
”);
如果$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
”);elseif ($day ==2 )print(“Tow turtledoves
”;elseif ($day ==1)print(“A partridge in a pear tree
”);
這種型式很常見,有一個特殊的elseif結構來處理它。 Four calling birds
”);elseif($day == 3)print(“Three French hens
”);elseif($day == 2)print(“Two turtledoves
”);elseif( $day ==1)print(“A partridge in a pear tree
”);
if、elseif…結構允許只在彼一個分支測試面成功時循序進行測試。前面的例子(一個結構帶五個分支,而不是巢狀嵌套的五個兩頭分支結構)在語法上是不同的,但行為是相同的。和HTML模式
從前面幾章中你已學過可以根據想要自由使用PHP標記在HTML模式和PHP模式間來回進行切換,無論如何都有是相當便利的。區塊的HTML文字,並且沒有動態程式碼或插入的變數,則切換回HTML模式並直接用文字來包含它,這樣就比用print或echo發送它更為簡單和有效。不太明顯的是,這個策略即使在本身條件結構內部也有效。也就是說,可以使用PHP決定要傳送什麼HTML,然後透過暫時切換回HTML模式來「傳送」這些HTML。
舉例來說,下面的程式碼使用print敘述建構了一個完整的HTML網頁(我們假設了一個female()的Boolean函數來測試它)。
The women-only siteIf(female()){Print(“TITLE>The women-only siteIf(female()){Print(“TITLE>The women-only site
” /HEAD>”);print(“This site has been specially constructed”);print(“for women only.
No men allowed here!”);}Else{Print(“TITLE>The men- only site
”);print(“”);
print(“This site has been specially constructed”);print(“for men only. No women allowed here!”);}?>我們可以不用所有這些print語法,而且兩部分支內部都使用HTML代碼,例如: phpIf(female()){?>
No men allowed here!
This site has been specially constructedfor men only.
No women allowed here!> BODY>
這個版本就更難閱讀了,但它們唯一的差別是把每個print敘述換成了以PHP右標記(?>)開始,和以PHP左標記(
在本書是例子中,我們盡量避免含有這種類型的條件,因為為樣對於PHP新手有些難以體會。當處於HTML模式中,PHP引擎要做的所有事情就只是傳遞字符,並等待下一個PHP左標記,這樣一定比解析再執行print敘述更快,尤其它們還包含有雙引號的字符串時更是如此)。 Switch 不是根據任意邏輯運算式進行分支,而是依照單一運算式的值來選擇不同的路徑。 Switch的語法如下,可選擇性的部分置於中包括號([])中:
switch( expression){case value -1:statement-2statement-2…[break;]case value--2statement-2…[break;] 2:statement-3statement-4…[break;]…[default:default-statement]}
expression可以是個變數,或是任何其它類型的運算式,只要它求值結果為一個簡單的值(整數、倍精度浮點數或字串)。此結構的執行方式是對 expression評算求值,然後測試其結果是否和某種情況的值相等。一旦找到一個匹配的值,後續的敘述則按順序執行,直到特殊的敘述“break;”,或者直到switch結構結束(正如我們後面將要看到的break還可以在迴圈結構中止與退出)。特殊的「default」標記可用在結構尾端,如果到它為止仍沒有其它情況匹配,則它就是與運算式「匹配」的那一段。
例如,我們可以重寫if-else的例子,如下:
switch($day){case 5:print(“Five golden rings
”);break;case4:printbreak;case4:print (“Four calling birds
”);break;case 3:print(“Three French hens
”);break;case 2:print(“Tow turtledoves
”);break;default:print( “A partridge in a pear tree
”);}
這個英文歌詞的例子會在2-5於之中輸出顯示相對應的該行,而在其它日期則打印“A partridge in a pear tree」。
Switch 讓人困擾的地方是,一個相符的case後的所有case都會執行,直到有break敘述來停止執行。在「partridge」的例子中,break敘述確保了一次只看到歌詞中的一行。如果去掉break敘述,則會看到幾行都被顯示出來。
迴圈
恭喜!你剛剛跨過了從script編製到「真正的程式設計」之間的界線了。到目前為止,我們看到的分支結構還算有用,但使用它們完成的計算還是有限的。另一方面,任何具有評算測試和無界限迴圈的語言都能夠比任何其它語言完成更好的功能,在電腦科學理論中,這一點是很確定的。在PHP中你也許並不需要寫一個C語言編譯器,不過請記住這裡沒有什麼天生的語言限制會阻止你這樣做。
以上就是PHP學習寶典-第七章的內容,更多相關內容請關注PHP中文網(www.php.cn)!