PHP3程序設計 之四

火星人 @ 2014-03-12 , reply:0


  第四章 程序控制

本章深入PHP內部,講述如何使用函數、表達式和語句以實現對程序的控制。

前面的章節初步介紹了怎樣操作數據,如果我們將操作數和操作符看作是構築元件的話,那麼它們組合起來即可形成
表達式。進一步講,表達式可以構成語句,語句用於組成函數,而函數則可用來組成程序。

提示:在學習有關編程語言的基本元素時,從全局理解--即理解這些元素是如何組成一個完整程序的--可能非常困難。
但也不必著急,樂觀一點。接下來的章節將逐步的顯示整個程序,並且一點一點的解釋它們是如何構造的。

4.1 表達式
當操作數和操作符組合到一起時,它們即組成表達式。本書的例子中已經展示了許多表達式,然而直到現在,我們才
開始將注意力集中在它們身上。

表達式是由一個或多個操作符連接起來的操作數,用來計算出一個值--標量或數組。

最基本的表達式就是數字:

12

從這個簡陋的開始,將逐步討論越來越複雜的表達式:

-12
-12 + 14
-12 + 14 * (24 / 12)
(-12 + 14 * (24 / 12))&& calculate_total_cost()

注意每個表達式,在不考慮複雜性的情況下,每一個表達式事實上是由較小的表達式和一個或多個操作數共同組成的。
當計算機編程者使用要定義的概念為該概念下定義時,這稱為遞歸。當一個遞歸完成時,表達式能被分成較更簡單的
部分,直到計算機能完全的執行每一部分。

4.1.1 簡單表達式
簡單表達式是由一個單一的賦值符或一個單一函數調用組成的。由於這些表達式很簡單,所以也沒必要過多討論。
下面是一些例子:
* initialize_pricing_rules() -- 調用函數。
* $str_first_name = 'John' -- 初始化標量。
* $arr_first_names = array( 'John', 'Marie') -- 初始化數組。

4.1.2有副作用的簡單表達式
表達式在它的主要任務之外,還有其它的副作用。當一個或幾個變數改變了它們的值,並且這些改變並不是賦值操作符
的操作結果時,就會出現這種副作用。例如,一個函數調用可以設置全局變數(全局變數是指在函數內部用global
關鍵字來指定的變數),或者加一操作符也可以改變變數的值。副作用會使得程序很難讀懂,因此編程的一個目標
就是應該儘可能地減少這種副作用。

不使用global關鍵字是避免副作用的一個好選擇。

讓我們看看以下有副作用的表達式例子:
* $int_total_glasses = ++$int_number_of_glasses
-- $int_number_glasses變數在加一以後,再把值賦予$int_total_glasses。
* function one() {global $str_direction_name; $str_directory_name = '/dos_data'; }
-- 當one()函數調用后,全局變數的值將被改變。

4.1.3 複雜表達式
複雜表達式可以以任何順序使用任意數量的數值、變數、操作符和函數。

儘可能使用簡短的表達式,這意味著它們更容易維護。

以下是一些例子:
* ((10+2) /count_fishes() * 114)
-- 包含有三個操作符和一個函數調用的複雜表達式。
* initialize_count( 20 -($int_page_number -1) * 2)
-- 有一個複雜表達式參數的簡單函數調用。

提示:有時很難分清左括弧和右括弧的數目是否相同。從左到右,當左括弧出現時,就加一,當右括弧出現時,就從
總數中減一。如果在表達式的結尾時,總數為零時,左圓括弧和右圓括弧的數目就一定相同了。

4.2 語句
所有的PHP程序都是由語句構成的,無論是簡單的語句,還是複雜的語句,這些語句按順序執行每一時刻執行一句,
直到遇到程序結束、跳轉語句、分支語句。
最基本的語句是:

;

此語句什麼也沒有做,但它仍是合法的。分號符是語句結束的標誌。如果需要,語句也可以相當複雜。例如:

$str_house_size =(
$int_number_of_rooms > 9 ?
"large" :
"small"
);

這行代碼在房子的數目大於9時,給$str_house_size賦予"large",否則給$str_house_size賦予"small"。
就象人類語言中的語句一樣,PHP語句可以分為幾個組成部分。在PHP中,組成部分可以是數值、變數、函數和關鍵字。
關鍵字是PHP留作自己用的單詞。這些關鍵字(_FILE_, _LINE_, if, else, elseif, while, do, for, break,
continue, switch, case, default, require, include, function),是組成PHP語言必不可少的,使用
它們可以控制程序的流程。
本書將不對關鍵字_FILE_和_LINE_作介紹,相關內容請參考PHP文檔。關鍵字require和include用來讀取和執行
PHP腳本, 在PHP手冊中有詳細介紹。以下部分將重點講述其餘的關鍵字。

4.2.1 語句類型
PHP共有6種類型語句,如表4.1所示。

表4.1 PHP數據類型
語句類型
描述
非執行語句
需要計算但不執行動作。
執行語句
執行某一動作。
賦值語句
給變數賦值。
判斷語句
判斷條件,並決定執行哪一個動作。
循環語句
重複執行一系列語句直到某條件為真或直到某條件為假為止。
跳轉語句
無條件改變程序流程到程序中另一行繼續執行。

非執行語句:
所謂非執行語句,就是PHP需要計算,但並不需要執行什麼動作的語句。例如:語句10 + 20,其值是30,但由於
沒有改變哪個變數值,所以也不需要作什麼動作。結果值將不保留,當下一條語句出現時,它很快就被丟棄了。
什麼動作也不需要做,那麼非執行語句又有什麼用呢?我不清楚,如果你能發現它們的用途請告訴我。
執行語句:
執行語句是通過表達式來執行某些動作。它們可以增加或減小某一變數值,或者調用一個函數。執行語句是PHP中
使用最多的一種語句類型。
賦值語句:
賦值語句並不複雜,它們可以給一個變數賦予一個數值。關於賦值操作符在前一章「PHP中的數據處理」中已經講過,
在此不再過多贅述。
判斷語句:
判斷語句使用if 和switch關鍵字,基於一個表達式的計算結果以決定執行某一段語句,或者基於表達式的結果在
兩段語句行中選擇執行其中的一個。例如,如果要處理的支票值大於1000美圓,則執行一段程序;如果支票數小於
1000美圓,就執行另一段程序。
if關鍵字
在if語句中通過計算表達式,得出真或假值,根據所求出的真、假值來決定執行哪一段程序。
最常見的有以下三種類型的if語句:
1.
if ( EXPRESSION ) {
// code block to be executed when
// expression is true.
}
2.
if ( EXPRESSION ) {
// code block to be executed when
// expression is true.
}
else {
// code block to be executed when
// expression is false.
}
3.
if ( EXPRESSION_1 ) {
// code block to be executed when
// expression_1 is true.
}
elseif ( EXPRESSION_2 ) {
// code block to be executed when
// expression_2 is true.
}
else {
// code blcok to be executed when
// all expression are false.
}

表達式可以包含第三章「PHP中的數據處理」中的任一個操作符,甚至可以是賦值操作符,這是因為賦值操作符要賦
的值,可能就是需要判斷的值。以上最後一個例子可能有點難以理解,現在讓我們再看一個例子:

$int_a = 10;
if ($int_a -= 5)
echo "a = $int_a";

這些代碼將顯示

a = 5

該if語句對表達式$int_a -= 5的處理是,$int_a的值先減5,結果值再賦給$int_a。如果結果值為真(即不為零),
則執行echo語句。
在這個例子中,除了說明了在只有一個需要執行的語句時,語句段外部的大括弧是可選的外,還說明了賦值操作符是
可以在if語句中使用的。

提示:即使僅有單個語句時可以不使用大括弧,通常還是要使用大括弧。這樣在以後增加語句時就更加方便。同時也
減少了在以後增加語句時忘了添加大括弧(這樣可能引起細微的邏輯錯誤)的可能性。

下面的代碼將說明else關鍵字是如何使用的:

$int_a = 5;
if ($int_a -= 5) {
echo "a = $int_a";
}
else {
echo "a is zero.";
}

這些代碼將顯示

a is zero.

elseif關鍵字按照以下的方法使用:

$str_name = 'John';
if ($str_name == 'Joe') {
echo "Your appointment is on Monday.";
}
elseif ($str_name == 'John') {
echo "Your appointment is on Wednesday.";
}
else {
echo "Your appointment is on Firday.";
}

這些代碼將顯示:

Your appointment is on Wednesday.

當變數$str_name即不是'Joe',又不是'John'時,將執行if語句的else子語句,否則,將執行其它兩個子語句中
的一個。通過把錯誤信息放在else語句塊中,以便顯示未知的值,else關鍵字對於捕獲未知或未預期的值也非常有用。
例如:

if ($str_input == 'A') {
// do A statements.
}
elseif ($str_input == '8') {
// do B statements.
}
else {
echo "Unrecognized Input Error: '$str_input' is unknown.";
}

在迄今為止所舉的例子中,所有if語句的子句中使用的都是同一個的變數。不過,正如下例給出的一個虛構的有關房子
和智能型計算機的例子那樣,可以充分發揮創造力:

if ($int_left_window_open == 1) {
$int_outside_temperature = check_outside_temperature();
if ($int_outside_temperature < 70) {
close_window('left');
}
}
elseif ($int_right_window_open == 1) {
$bln_mail_exists = look_outside_check_mailbox();
if ($bln_mail_exists) {
make_announcement("The mail is here.');
}
}
else {
$str_window_side = select_side_of_house();
open_window($str_window_side);
}

switch關鍵字
如果需要同時測試、判斷多個條件值時,if語句處理起來就顯得比較煩瑣--因為所有的elseif語句都要執行過一遍。
在這種情況下許多人會發現,使用switch語句會更容易、快速。
switch語句句法結構如下

switch ( VARIABLE ) {
case VALUE1:
break;
case VALUE2:
break;
case VALUEn:
break;
default:
break;
}

在switch語句中,每一個需要檢查的值都對應一個相應的case 語句。被檢查的變數可以是任意標量值,(也就是說,
數字和字元串都可以被檢查)。
如果case語句中沒有break關鍵字,那麼PHP將執行下一個case語句;如果這個case語句中仍沒有break語句,那麼
就接著執行再下一個case語句,如此下去直至找到break關鍵字為止。
下面給出一個簡短但很完整的例子,講的是用switch語句來處理命令的命令行程序。每當用戶輸入一條命令,就會
調用switch語句,以決定該執行什麼任務。當然,PHP常用於創建Web瀏覽器程序,可能從來不會這樣使用PHP。

switch ($str_input) {
// The print and echo case perform the same task,so
// the print case needs no break keyword.
case 'print':
case 'echo':
// do the echo task.
break;
case 'check_balance':
// do the check balance task.
break;
case 1:
case 2:
// add $str_input to something.
break;
default:
echo "You have entered an unrecognized command or are ";
echo "trying to add a number other than 1 or 2.";
break;
}

使用分支語句的限制是,只能對一個變數進行判斷。從另一角度看,這種限制剛好就是switch語句比if語句容易讓人
理解的原因。

循環語句:
循環語句就是根據程序需要,重複執行一段程序直到一個指定的表達式值為真或假為止。
PHP中有三個控制程序循環的關鍵字,分別是for,while和do,且這三個關鍵字處理的循環語句又有細微的差別。但是,
無論哪種請況,表達式值都是用來決定循環語句何時應該停止的。使用關鍵字for的循環語句最為複雜,讓我們首先來看它。
for關鍵字:
從句法結構講,for語句是由三個表達式和一段語句組成的。形式如下:

for (INITIALIZATION; CONDITION; OPERATION) {
// statement block
}

在循環語句開始之前,首先需要對表達式進行初始化。此時的初始化適用於任意變數,但大多數編程者只對循環語句
段中要用到的變數進行初始化。初始化工作可以在for語句之前進行,之所以在for語句內部執行對表達式的初始化工作,
是為了有助於生成一自我完備的程序。

提示:初始化表達式通常是賦值表達式。注意在初始化賦值時,不要將等於操作符(==)和賦值操作符(=)搞混淆了。
否則,這將會在程序中留下隱患。例如:$iindex == 0就是錯誤的,正確的表達式應該是$iindex = 0。

條件表達式用於控制循環部分是繼續執行,還是停止循環。當條件表達式值判斷為假時(就是說為零),則循環終止。

循環變數是條件表達式中使用的變數,用於控制循環什麼時候結束。

運算表達式用於在每次執行完循環內部的代碼段以後,以某種方式修改在條件表達式中使用的變數(即循環變數)值。
for語句的最基本使用方法就是從零開始計算,直到某一值為止。例如:

for ($loop_variable = 0;
$loop_variable < 100;
$loop_variable++) {
echo "Inside Loop: loop_variable = $loop_variable";
}

該語句顯示了從0到99的數字:

Inside Loop: loop_variable = 0
Inside Loop: loop_variable = 1
...
Inside Loop: loop_variable = 98
Inside Loop: loop_variable = 99

當循環語句結束時,變數$loop_variable值為100,但這個值不再顯示出來,這是因為列印完99以後,又對表達式
進行加一運算,得到的變數值為100。這時,判斷條件表達式值為假,循環結束。

注意:很關鍵的一點是,運算表達式(或語句段中的代碼)必須要改變循環變數的值,或使用本章稍後講到的break
關鍵字。否則,循環語句將永遠不會結束而成為一死循環。

for循環語句也可以通過對表達式進行遞減運算來減小循環變數值:

for ($loop_variable = 100;
$loop_variable < 0;
$loop_variable --) {
echo "Inside Loop: loop_variable = $loop_variable";
}

該語句執行後會輸出從100到1的數字:

Inside Loop: loop_variable = 100
Inside Loop: loop_variable = 99
...
Inside Loop: loop_variable = 2
Inside Loop: loop_variable = 1

在初始化表達式中能通過逗號隔開的方法對多個變數進行初始化賦值,下例給出了如何對多個變數初始化,以及怎麼在
一個循環語句中再包含另一個循環語句

for ($row = 0;$row <=2; $row++) {
for ($col_value = 0, $col = 0; $col <= 2; $col++) {
$col_value += $row + $col;
echo "[$row,$col] = $col_value";
}
}

顯示輸出為:

[0,0] = 0
[0,1] = 1
[0,2] = 3
[1,0] = 0
...
[2,1] = 5
[2,2] = 9

每次啟動內部循環時,$col_calue就會重新初始化為零。
迄今為止,我們只講了循環變數遞加一或遞減一的情況。然而,運算表達式是十分靈活的,可以按需要以任何方式
改變循環變數值,也可以通過用逗號隔開的辦法同時進行多個操作。例如:

$int_number_of_items = 10;

// The following for loop places each
// expression in the loop header on a separate
// line to enhance readability.
For ($first_time = 1,$index =1;
$index <= $int_number_of_items;
$index += 2, $first_time = 0
) {

if ($first_time) {
echo "Report Header";
}

each "Report Line $index";

}
if (! $first_time) {
echo "Report Footer";
}

代碼行輸出顯示如下:

Report Line 1
Report Line 3
Report Line 4
Report Line 5
Report Line 9

Report Footer

注意,每重複一次循環以後,循環變數($index)會遞增2。另外,變數$first_time控制報告的頁眉和頁腳的輸出。
如果改變變數$int_number_of_items的初始值為零的話,那麼什麼也不會顯示出來。第一次對條件表達式值進行
判斷時,變數$index值為1,變數$int_number_of_item值為0,所以不會進入語句段,也不執行運算表達式。這導
致變數$first_time值仍是1,在循環結束時,也不會輸出報告的頁腳。

while關鍵字:
當條件為真時,while循環重複一段語句塊。如果條件在執行while語句時不為真,語句塊就不會被執行。
while循環的語法如下
1.
while ( CONDITION ) {
// statement block.
}
2.
while ( CONDITION ) :
// statement block.
Endwhile;

以下例子顯示了使用while語句是如何簡單:

$iindex = 0;
while ( $iindex < 5 ) :
echo "inside while statement: $iindex";
$iindex++;
endwhile;
echo "outside while statement: $iindex";

此例子結果將顯示

inside while statement: 0
inside while statement: 1
inside while statement: 2
inside while statement: 3
inside while statement: 4
outside while statement: 5

注意當while語句完成以後,$iindex的值為5 -- 而不是條件表達式($iindex < 5)中所希望的4。當$iindex
為4時,條件表達式為真,將執行語句塊,然後$iindex值賦予5,這時條件語句再次執行,而表達式為假,語句結束。
如果$iindex有大於5的值時(隨便說一個數,8),當運行到while語句時,以上的例子將顯示

outside while statement: 8

do關鍵字:
當表達式為假時,do循環執行語句塊。此語句的測試條件正好和while循環相反,while循環測試表達式是否為真,
並且條件表達式是在語句塊執行后測試,而不是在語句塊前測試,這也是do循環和while循環的不同點,這意味著語
句塊至少執行一次。
do循環的語法如下

do {
// statement block
} ( CONDITION );

幾乎所有的循環都可以用while語句或者do語句來表示。喜歡怎樣表達程序的邏輯結構,覺得哪種方法使用起來更順手,
都無關緊要。有時,使用do print_the_page while the number of pages is less than 20這樣表述比while
the number of page is less than 20的表述更易於理解。
以下do語句是本章較早使用while表述例子的另一個版本。

$iindex = 0;
do {
echo "Inside Do Statement: $iindex";
$iindex++;
} while ($iindex < 5);
echo "Outside Do Statement: $iindex";

此例子的結果將顯示

Inside Do Statement: 0
Inside Do Statement: 1
Inside Do Statement: 2
Inside Do Statement: 3
Inside Do Statement: 4
Outside Do Statement: 5

象while循環一樣,$iindex變數的結束值也為5。然而和while循環不一樣,如果$iindex的啟始值為8時語句塊至少
會執行一次。當$iindex的初始值為8時,將顯示以下行:

Inside Do Statement: 8
Outside Do Statement: 9

跳轉語句:
PHP提供了兩個語句以幫助控制循環的行為:break和continue。
break關鍵字:
break關鍵字使PHP停止執行當前的語句塊,從緊跟著當前語句塊的語句開始運行。下面的例子顯示了如何在一個簡單
的for循環中使用break語句。

清單4.1 break.php3--使用break語句從循環中退出


此腳本結果將顯示:

0
1
2
After the loop: index=3

break語句的另一個方面是令人討厭的,在循環語句結束以後(在上面代碼的B位置),將沒有辦法分辨循環語句結束
是由於break語句結束的,還是以自然方式結束的。至少,在沒有測試$index的值前是沒有辦法的,這個值依賴於引
發break語句而使循環結束的條件。對於不同的循環測試$index變數都會有所不同。因此在沒有註釋的情況下,循環
后測試$index變數是非常令人費解的。
為了解決這個問題,可以使用一個變數作為標誌。如果變數值為1時(意味著同意),那麼執行break語句;否則,循
環是正常終止的。下一個例子,清單4.2顯示了如何使用標誌變數。

清單4.2 flag.php3--在break語句中使用標誌變數


此腳本結果將顯示:

0
1
2
Loop ended naturally.

我們還需要進一步探索break語句的其它方面,就是如何從嵌套的循環中退出。清單4.3顯示了如何使用這種技術。

清單4.3 nested_break.php3--從嵌套的循環中退出


此腳本結果將顯示:

[0,0]
[0,1]
[0,2]
[0,3]
[0,4]

正如所看到的,給break語句增加的表達式是告訴PHP需要退出幾重嵌套。

continue關鍵字:
continue關鍵字中止循環的當前重複,並立即開始下一個重複。在for循環的情況下,下一個重複在操作表達式處開始。
清單4.4顯示了運行中的continue關鍵字。

清單4.4 continue.php3--使用continue關鍵字


此腳本結果將顯示:

0
1
2
4

請注意3沒有顯示出來。在語句塊內部的if語句使得該語句塊停止運行,並且在for循環的操作子句處重新開始。這樣,
在循環的第三次重複時,將忽略echo語句。
當使用嵌套的循環時,可以在continue語句中使用表達式,如清單4.5所示。

清單4.5 nested_continue.php3-在嵌套循環中使用continue語句


此腳本結果將顯示:

[0,0]
[0,1]
[1,0]
[1,1]
[2,0]
[2,1]

請注意內部循環總是在2結束,echo "";語句也從沒有執行(由於沒有輸出空行)。因此,conutinue 2語句
一定是停止了內部循環,並且從外部for循環的操作子句$row++重新開始。
如果的確需要在內部for循環完成以後顯示空行,該怎麼辦呢?清單4.5顯示了僅僅簡單的把echo語句放到for內部循
環的結尾是不夠的,這是因為continue語句會忽略此語句。可以考慮在外部for循環的操作子句的後面使用逗號。例如:

for ($row = 0; $row < 3; $row++ ,echo "") {
for ($col = 0; $col < 3; $col++) {
if ($col == 2) {
continue 2;
}
echo "[$row,$col]";
}
}

以上循環將顯示:

Parse Error: parse error in
nested_continue.php3 on line 3

發生什麼了?很明顯,語法規則禁止在操作子句中使用echo命令。取而代之,可以使用print命令:

for ($row = 0; $row < 3; $row++ ,print "") {
for ($col = 0; $col < 3; $col++) {
if ($col == 2) {
continue 2;
}
echo "[$row,$col]";
}
}

此腳本結果將顯示:

[0,0]
[0,1]

[1,0]
[1,1]

[2,0]
[2,1]

4.3 函數
函數幫助程序員組織自己的代碼,使其成為比較容易理解和使用的代碼段。函數讓程序員一步步地編寫出程序,並以
這種方式測試代碼。
在對所編程序有一個初步構想之後,會需要在腦子裡或在紙上列出編程大綱。所列大綱中的每個步驟,可能就是一個
函數,這就稱之為模塊化的程序設計,這種技術將編程的詳細過程隱藏起來,以便讀源程序的人明白整個的程序的設
計目標。
例如,如果在所編程序中包含有一個計算圓面積的函數,可以調用如下一行程序編碼:

$flt_area_of_circle = area_of_circle(5);

讀者在看到所調用的函數以後,一般都會明白程序在做什麼了,而對函數的實際內容並不需要過多了解。

提示:細心設計的函數和變數名將有助於理解程序。如果出現$areaFC = areaCirc($fRad)這樣的代碼,其含義就
不是十分清晰。

注意:調用函數意味著PHP將停止程序當前行的執行,跳轉到所調用的函數中去。在函數執行完畢以後,PHP會重新跳
回到程序中調用函數的地方,繼續往下進行。

讓我們更清楚地看一看函數調用,在命令行中首先出現的是一個標量和一個賦值操作符。應該知道其作用是將賦值
操作符右邊的數值賦予變數$flt_area_of_circle,但是賦值符右邊的究竟是些什麼呢?
最先看到的是函數名:area_of_circle(),緊跟其後的圓括弧表示這是一次函數調用,在圓括弧中的是準備傳給函數
的一些參數或數值。可以將參數設想為一個足球,傳球時,接收方(比如函數)會有幾種選擇:運球(以某種方式進行
修改),傳球出去(調用其他程序),犯規(調用錯誤處理程序)。
一個函數的語法結構如下:

function functionName ( parameterList ) {
// lines of code
}

函數取名有幾個規定 -- 其中最重要的一條就是,函數名不能以數字開頭,且中間不能有空格。參數表可以任選,它
提供了函數可以使用的特定數值。

4.3.1 函數返回值
每一個PHP函數都會返回一個數值給調用者。畢竟,在設計一個計算圓面積的函數時,其意義就在於需要將該值賦予
某個變數。清單4.6給出了一個程序,它定義並調用area_of_circle()函數。

清單4.6 area_of_circle.php3--計算圓的面積



該程序輸出如下:

The area is 78.5375.

該例顯示了是如何將一個參數傳給函數的(記得足球的比喻嗎?)。參數是在緊跟函數名後面的圓括弧中給定的。在
表4.6中的函數調用語句是 area_of_circle(5),其中只給出了一個參數,即數字5。一旦傳給函數以後,它就認為
是變數$flt_radius的值了。
該函數的第一行為:

return(3.1415 * ($flt_radius * $flt_radius));

它計算圓的面積,並將計算得出的新值返回。事實上列印出的內容說明調用area_of_circle()函數以後的程序流程
已經返回,並且計算得出的值將賦給變數$flt_area_of_circle。

注意:一些程序設計語言在函數和子程序之間有些差別,區別在於函數會返回數值,而子程序不會。PHP中不存在這
種差別,不管是否返回數值都是函數。

調用函數后返回一個數值是一個不錯的特點,同時這也是PHP最關心的事情。你也能夠返回一個數組,在清單4.7中
定義了一個叫做create_list的函數,其返回值賦給變數$temp,結果輸出在圖4.1中給出。

清單4.7 create_list_php3--如何從函數返回數值列表




Page 91 圖4.1

圖4.1 顯示函數create_list()的返回值

本例中,將整個返回的數組值賦給了一個變數,有時,這樣做並不是處理返回數組的最簡便方法。如果使用PHP的
list功能,就能夠將每一個返回單元值賦予相應的變數,例如:

list( $a, $b, $c ) = create_list();

該賦值語句執行以後,$a,$b和 $c的值分別是100,200和300,因為函數中沒有指定變數存放第四個數組元素,
所以其值丟掉了。
到目前為止,我們只注意了簡單數組--即在程序中沒有定義下標/數值對的數組。如果返回的數組指定了下標的話,
PHP的反應就會有所不同。在清單4.8中對返回的數組作了修改,所以第二個數組元素就有了指定的下標,在圖4.2中
給出了該程序的結果輸出。

清單4.8 create_list2.php3--如何讓函數返回數值列表

200, 300, 400 );
}

list ( $a, $b, $c ) = create_list();

dump_array( create_list() );

echo "a=$a";
echo "b=$b";
echo "c=$c";
?>


Page 92, 圖4.2

圖4.2 具有指定下標的數組返回結果

注意,正如所預期的那樣,賦給$b的值是300,而不是200。使用list賦值時,指定下標的元素被忽略了。

4.3.2 向函數傳遞參數
一般來說,PHP會將參數的值傳遞給函數,這就意味著函數不能改變參數表中任何變數的值。在看過下例以後,就會
比較清楚為什麼會這樣了:

function one( $parameter ) {
$parameter++;
}

$a = 10;
one($a);
echo "a=$a";

如果函數one()中的加一操作對變數$a有影響的話,那麼可以預期函數echo的輸出結果為 a=11,而事實上並非如此。
與此相反,本例的輸出顯示為a=10。
設計要改變其參數的函數並不是一個好主意。如果要弄明白這是為什麼,就要研究一下編程原理。當兩段代碼共享信息
時,它們就被稱為是緊耦合的,此時當一段程序改變時--比如加入某種功能--很可能,另外一段程序也要跟著改變。
因為兩段程序需要同時進行改動,所以這時發生差錯的幾率就比較高。另一方面,如果編製的兩段程序的關係是松耦合
的話,那樣,對程序的修改是相互隔離的,所以發生錯誤的幾率相應就會降低。只有在某些特定的情況下,有些函數需
要修改其參數值。
當函數必須要修改其參數時,那些參數需要通過引用的方式傳遞給函數。在函數中使用變數引用作為參數時,提供的是
存放變數的內存地址,下面給出的函數one就是使用引用變數的例子:

function one( &$parameter ) {
$parameter++;
}

$a = 10;
one($a);
echo "a=$a";

這段小程序的結果輸出為a=11,表明函數one()改變了變數$a的值。注意,採用引用變數的唯一不同就是,需在所
定義函數的參數名前面加上&號。
如果使用另一個程序員編的程序,就不要奢望去改變函數的定義。在那種情況下,可以在所調用函數的參數前面加上
一個&號,如下示:

one(&$a);

在結束參數傳遞這一個話題之前,有必要大概講一講數組作為函數的參數傳遞的情況,下例顯示了如何從函數中傳遞
出數組元素值:

function array_first( $arr_parameter ) {
return($arr_parameter[0]);
}

$a = array_first( array(3,5) );
echo "a=$a";

在這個例子中定義了一個查找第一個數組元素的函數。請注意,在此用到了標準的數組元素記號。函數使用數組作為
參數和使用標量作為參數的情況並無太大的區別。
4.3.3 給函數賦予預設值
PHP允許為函數的一些或全部參數賦予預設值,這一特性使得函數使用起來更加容易理解,因為這樣一來,函數調用
時用到的參數就會很少。在清單4.9中定義了函數font,其中提供了顏色和大小等參數的預設值。圖4.3顯示了運行
該程序后的輸出結果。

清單4.9 default_values.php3--給參數賦預設值

$str_text";
}
?>








注意:賦予預設值的變數總是放在所定義函數參數的最後幾個位置。


Page 95,Figure 4.3

圖4.3 提供預設值的font()函數

4.3.4 控制變數的作用域
所謂變數的作用域,指的是在程序中哪一部分可以看到並使用這一變數。PHP只認可兩種類型的作用域 -- 頁面和函數
作用域。頁面作用域是指變數適用於整個 Web頁面,函數作用域是指變數適用於單個函數。正常情況下PHP變數只在
它們的定義的作用域範圍內才可以使用。
當需要在函數內部定義頁面作用域的變數時,可以使用如下所示的global關鍵字:

function one( ) {
global $a;
$a++;
}

$a = 10;
one($a);
echo "a=$a";

此函數結果將顯示a=11,表明了在one()函數內部可以使用頁作用域的$a變數。使用global關鍵字與傳遞引用變數相
類似,因為在這兩種情況下都要改變函數外部的變數。

注意:在程序中要使用global關鍵字之前,請慎重考慮一下程序的設計思路,或許使用函數參數同樣可行。在函數內部
使用頁面作用域的變數,並不是最好的程序設計方法,因為函數的內部代碼應該與程序中的其它部分相隔離。試想去讀
一段在多個(或者每個)函數中都能改變頁面作用域變數的程序 - 它很難讀懂的,不是嗎?

4.3.5 嵌套函數調用
函數可以嵌套調用,也就是說一個函數可以調用另一個函數,而被調用的函數又可以調用另一個函數,然後還可以再
調用另外的函數,如此等等。準確地講,函數能嵌套層數的多少由很多因素決定。一般來說,不必考慮任何限制因素。
清單4.10講述了一個應用嵌套函數調用的程序。

清單 4.10 nested_function.php3--嵌套函數調用是非常有用的



該程序輸出顯示如下:

a = 37

這個例子只是為了講解嵌套函數的調用而做的,並沒有什麼實際的用處。函數increment_by_three增加其參數值后,
又調用函數increment_by_two,最後導致總的增量為3。
使用嵌套函數調用最大的優點是可以將程序設計分為幾個部分。每一個任務轉化為一個嵌套的函數調用,例如:

function do_task() {
initialize_data();
read_data_from_database();
process_data_to_database();
perform_cleanup();
}

4.3.6 遞歸函數
調用自己的函數就叫做遞歸函數。可以回憶一下數組也是可遞歸的,(例如,一個數組中也可以包含另一個數組)。
為了演示遞歸究竟是怎麼一回事,讓我們先來看看數學上著名的數字序列--Fibonacci 序列。Fibonacci序列是這樣的:

0,1,1,2,3,5,8,13

Fibonacci序列中的每一個數字都是由前兩個數字相加得出的。例如,第八個數字13就是5和8相加的結果值。清單4.11
包括了Fibonacci函數,它是將序列順序號作為參數,返回與該順序號相關的Fibonacci數字。

清單4.11 fibonacci.inc--使用遞歸函數計算Fibonacci數



可以用如下方式調用Fibonacci函數:



讓我們來檢查一下當參數為4時調用Fibonacci函數的執行結果。Fibonacci函數每調用自己一次,就是又一級遞歸
調用。第零級是對該函數的初始調用:

Level 0:

$n = Fibonacci(4);

因為4大於3,執行最後一個return語句,併產生新的一級遞歸調用。

Lelel 1:

return( fibonacci(3) + fibonacci(2) );

此時,函數遞歸了兩次,一次使用的參數是3,一次參數為2。這些Fibonacci調用以後,因為3和2都不小於2,所以
又產生了新一級的遞歸調用:

Level 2:
return( fibonacci(2)+fibonacci(1) +
fibonacci(1)+fibonacci(0) );

Level 3:
return( fibonacci(1)+fibonacci(0)+1 + 1+0 );

Level 2:
return( 1+0+1 + 1+0);

Level 1:
return( 2 + 1 );

Level 0:
return( 3 );

如上例所示,遞歸展開時,每一級對函數遞歸調用的結果又會用於再高一級的遞歸調用中。Level 0的遞歸結果是3。
非常重要的一點是每一個遞歸函數都需要一個辦法讓遞歸停止。有意義的是,因為數組可以遞歸,所以使用數組變數
的函數也必須可以遞歸。清單4.12給出了一個叫做array_dump的函數,結果顯示了一個PHP數組的下標/數值對。

清單4.12 array_dump.inc--使用遞歸函數顯示數組的下標/數值對



用如下方式調用函數array_dump:

'Waswaldo'
,'address' => array(
,'line1' => 'Suite 300'
,'line2' => '43 Broad Lane'
)
);

// display the array.
Array_dump($arr_test);
?>

數組$arr_test的輸出結果如圖4.4所示。


Page 101,圖4.4

圖 4.4使用函數array_dump顯示數組

4.4 總結
本章涉及了許多內容,著重講述了PHP的表達式、語句和函數。所有這些都為後面開始學習更有意義的應用程序開發
工作之前,提供了必要的最基本知識。
我們已經學過,表達式是任意操作數和操作符的組合。操作數可以是變數、數字或函數調用。一些使用遞增或遞減
操作符的函數可能會有某些副作用。正如本章中在討論語句時所看到的那樣,表達式可用來控制判斷語句、循環語句
和跳轉語句的。
語句可以是一個以分號結束的單個表達式。例如,$n = 5;就是一個賦值語句。語句也可以比較複雜,它們可能是
判斷語句、循環語句和跳轉語句。
if語句和switch語句都屬於判斷語句,這兩種語句都是先對表達式進行計算,然後根據表達式的值決定執行哪一段語句。
循環語句包括for、while和do循環語句,它們的目的是為了多次執行同一段語句。for循環語句最複雜 -- 它包括初
始化、條件和操作子語句幾部分,初始化子語句在for循環開始時執行,當條件表達式為假時,for循環結束,而每次
循環之後都要執行操作表達式。對while循環語句來說,因為要先對條件表達式值進行計算判斷,所以有可能一次也不
執行其語句段。相反,do循環則總是要先執行其語句段,而後再對其條件表達式進行計算判斷。
函數的使用使得程序更加容易處理。函數能夠接收多個參數,並且能給參數設置預設值。一個函數的內部變數只可以只
在函數的內部使用。當要使用作用域為整個Web頁的變數時,就要使用關鍵字global進行定義。
當處理數組或遇到一些其它的數學概念時,可能要使用到遞歸函數。遞歸函數是自己調用自己的函數。記住所有的遞歸
函數都必須具有能在需要時結束遞歸的方式。
下一章「中場一:連接資料庫」,將從學習PHP的基本知識中中斷一下。將學習如何連接MySQL資料庫以及如何處理HTML表單。




[火星人 via ] PHP3程序設計 之四已經有128次圍觀

http://www.coctec.com/docs/program/show-post-72470.html