# 第五章
## 語句
如果說數據類型是Pascal 編程的一個基礎,那么另一個則是語句。編程語言的語句主要由關鍵字和操作指令組成。語句常放在過程或函數中,就象我們將在下一章中看到的那樣。現在,我們集中講解最基本的編程語句。
### 簡單語句和復合語句
Pascal 簡單語句中不包含任何別的語句,賦值語句和過程調用即是簡單語句的例子。簡單語句用分號隔開,如下所示:
~~~
X := Y + Z; // assignment
Randomize; // procedure call
~~~
用begin 和end 將簡單語句括起來即組成復合語句,復合語句用法與普通的Pascal 語句相同,見下例:
~~~
begin
A := B;
C := A * 2;
end;
~~~
end之前的最后一條語句末尾分號不是必需的,你可以寫成:
~~~
begin
A := B;
C := A * 2
end;
~~~
這兩種寫法都是正確的。第一種多了一個無用(但也無害)的分號。分號實際上是一個空語句,也就是說,是一個沒有代碼的語句。有時,空語句可用在循環體或其他特殊情況中。
注意:雖然最后一條語句末尾的分號沒有用,我卻總是加上它,并且建議你也這樣做。因為有時你可能需要在末尾添加語句,如果最后沒有加分號,你就必須記著加上它,與其如此不如一開始就加上它。
### 賦值語句
在Pascal 語言中賦值語句用冒號-等號操作符“:=”,對使用其他語言的編程人員來說這是一個奇怪的符號。在其他語言中用作賦值符號的“=”在Pascal 中用作關系運算符,用于判斷是否相等。
> 注意:賦值和相等判斷使用不同的符號,使Pascal 編譯器(象C編譯器一樣)能更快解譯源代碼,因為這樣就不需要通過檢查上下文來判斷符號的意義,此外使用不同操作符也使代碼更易讀。
### 條件語句
條件語句通過條件檢測,判斷是否執行該條件語句中包含的語句。條件語句可有兩種基本形式:if語句和case語句。
### If語句
對if-then型語句,僅當條件滿足時,語句才執行;對if-then-else型,if語句在兩條語句中選擇一條執行。條件用布爾表達式建立,這里通過一個簡單的Delphi 例子來示范如何寫條件語句。首先,創建一個應用程序,在form上面放兩個復選框(check box)和四個按鈕(button),不要改變復選框和按鈕的名字,雙擊按鈕為其OnClick 事件添加響應程序。下面是第一個按鈕事件代碼中一條簡單的if語句:
~~~
procedure TForm1.Button1Click(Sender: TObject);
begin
// simple if statement
if CheckBox1.Checked then
ShowMessage ('CheckBox1 is checked')
end;
~~~
當點擊button1,如果第一個復選框中有復選標記,那么這個程序將顯示一條消息(見圖5.1)。我用了ShowMessage 函數,因為它是Delphi中最簡單的短信息顯示函數。
**圖 5.1: 例IfTest顯示的信息**

如果點擊按鈕后沒有反應,表明復選框未被選中。對于這種情況,最好能交代得更清楚些,為此在第二個按鈕的代碼中,我用了if-then-else 語句:
~~~
procedure TForm1.Button2Click(Sender: TObject);
begin
// if-then-else statement
if CheckBox2.Checked then
ShowMessage ('CheckBox2 is checked')
else
ShowMessage ('CheckBox2 is NOT checked');
end;
~~~
要注意的是,不能在第一句之后、else 關鍵詞之前加分號,否則編譯器將告知語法錯誤。實際上,if-then-else 語句是單純的一條語句,因此不能在語句中間加分號。
if 語句可以很復雜,句子中的條件部分可以是一系列條件(用and、 or 、 not等布爾操作符聯接起來),if語句又可以嵌套另一個if語句,見例IfTest中其它兩個按鈕的示范代碼:
~~~
procedure TForm1.Button3Click(Sender: TObject);
begin
// statement with a double condition
if CheckBox1.Checked and CheckBox2.Checked then
ShowMessage ('Both check boxes are checked')
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
// compound if statement
if CheckBox1.Checked then
if CheckBox2.Checked then
ShowMessage ('CheckBox1 and 2 are checked')
else
ShowMessage ('Only CheckBox1 is checked')
else
ShowMessage (
'Checkbox1 is not checked, who cares for Checkbox2?')
end;
~~~
仔細閱讀代碼并執行程序,看看你能不能理解整個程序。當你搞不清某種編程結構時,可以先寫一個簡單程序,這樣可以幫你學習許多東西。你可以再加幾個復選框,增加這個簡例的復雜程度,并進行各種測試。
### Case語句
如果你的if語句變得非常復雜,有時可以用case語句代替它。case語句包括用來選值的表達式、可能值序列或一個取值范圍。這些值應該是常量,并且它們必須唯一,而且應屬于有序類型。Case語句最后可以帶一個else 語句,當沒有一個標簽與選擇器的值一致時,執行else語句。下面是兩個簡單的例子:
~~~
case Number of
1: Text := 'One';
2: Text := 'Two';
3: Text := 'Three';
end;
case MyChar of
'+' : Text := 'Plus sign';
'-' : Text := 'Minus sign';
'*', '/': Text := 'Multiplication or division';
'0'..'9': Text := 'Number';
'a'..'z': Text := 'Lowercase character';
'A'..'Z': Text := 'Uppercase character';
else
Text := 'Unknown character';
end;
~~~
### Pascal語言中的循環
其它編程語言中使用的循環語句,Pascal語言中都有,它們包括 for、 while 和 repeat 語句。如果你用過其他編程語言,你會發現Pascal中的循環語句沒什么特別的,因此這里我只作簡要的說明。
### For循環
Pascal 中的for循環嚴格地建立在計數器基礎上,循環每執行一次,計數器不是增加一個值就是減小一個值。下面是一個for語句的簡例,用來將前十個數加起來:
~~~
var
K, I: Integer;
begin
K := 0;
for I := 1 to 10 do
K := K + I;
~~~
同樣的for語句可以用正好相反的計數器來寫:
~~~
var
K, I: Integer;
begin
K := 0;
for I := 10 downto 1 do
K := K + I;
~~~
Pascal 中的for循環語句其靈活性比其他語言小(它不能指定1之外的步長),不過簡單也容易理解。如果需判斷的條件比較復雜,或想自定義計數器,你可以用while語句或 repeat 語句,而不是for循環語句。
注意:for循環計數器不必非是數字,它可以是任何有序類型的值,例如一個字符或一個枚舉類型值。
### while語句和repeat語句
while-do 循環語句和 repeat-until 語句的不同點在于repeat 循環語句的代碼至少要執行一次。從下面的簡例很容易理解這一點:
~~~
while (I <= 100) and (J <= 100) do
begin
// use I and J to compute something...
I := I + 1;
J := J + 1;
end;
repeat
// use I and J to compute something...
I := I + 1;
J := J + 1;
until (I > 100) or (J > 100);
~~~
從上可見即使 I 或 J 的初始值大于100,repeat-until循環中的代碼也仍會執行一次。
> 注意:兩種循環另一個關鍵的不同點是,repeat-until 循環的條件是反向的條件,只要不滿足這個條件,循環就執行;當條件滿足時,循環終止。這正好與while-do 循環相反,while-do 循環當條件是真值時才執行。為此,我不得不在上面代碼中用反向條件來獲得相同的結果。
### 一個循環語句例子
為了探究循環的細節,讓我們看一個Delphi 簡例,這個循環例子表現了固定計數器循環和隨機計數器循環之間的差別。建一個新的工程,在主窗體上放一個listbox和兩個button,通過設置Object Inspector中的name屬性分別命名button為BtnFor 和BtnWhile。你還可以把Caption 屬性中的Btn 去掉,或甚至加上 & ,讓跟在 & 后面的字母成為快捷鍵。下面是該窗體文本描述:
~~~
object Form1: TForm1
Caption = 'Loops'
object ListBox1: TListBox ...
object BtnFor: TButton
Caption = '&For'
OnClick = BtnForClick
end
object BtnWhile: TButton
Caption = '&While'
OnClick = BtnWhileClick
end
end
~~~
**圖 5.2: 單擊For按鈕后顯示的結果**

現在,我們分別給兩個button 添加OnClick 事件代碼。第一個button用一個簡單的for循環來顯示一列數字,結果如圖5.2。這個循環向listbox中的Items 屬性添加一系列字符串。在執行循環之前,需要清除listbox 中的內容。程序如下:
~~~
procedure TForm1.BtnForClick(Sender: TObject);
var
I: Integer;
begin
ListBox1.Items.Clear;
for I := 1 to 20 do
Listbox1.Items.Add ('String ' + IntToStr (I));
end;
~~~
第二個button的事件代碼稍微復雜點。本例中讓while 循環基于一個隨機增長的計數器。為實現它,我調用了Randomize 過程, 用它來重置隨機數發生器,還調用了Random 函數, 其取值范圍為100, 即函數返回0至99之間的隨機數,隨機數序列控制while 循環的執行次數。
~~~
procedure TForm1.BtnWhileClick(Sender: TObject);
var
I: Integer;
begin
ListBox1.Items.Clear;
Randomize;
I := 0;
while I < 1000 do
begin
I := I + Random (100);
Listbox1.Items.Add ('Random Number: ' + IntToStr (I));
end;
end;
~~~
每次點擊While按鈕,出現的數字都不同,因為這些數字取決于隨機數發生器。圖5.3顯示了兩次點擊的結果,可看到不僅每次產生的數字不同,而且數據項數也不同。也就是說,這個while循環執行的次數是隨機的。
**圖 5.3: 按While按鈕后顯示的結果 **

> 注意:用 Break 和 Continue 系統過程可以改變循環執行的標準流程。Break 中斷循環;Continue直接跳至循環測試句,或使計數器增加一個步長,然后繼續循環(除非條件為空或計數器達到最大值)。還有兩個系統過程 Exit 和 Halt,讓你立即從函數或過程中返回,或者終止程序。
### With語句
我要講的最后一種Pascal 語句是With語句,With語句是Pascal編程語言獨有的語句,不過最近JavaScript 和Visual Basic也添加了這種語句,它在Delphi程序設計中很有用。
With語句是一種用于簡化代碼的語句。如你要訪問一個記錄類型變量(或一個對象),用With語句就不必每次重復變量的名字。例如對于以下的記錄類型代碼:
~~~
type
Date = record
Year: Integer;
Month: Byte;
Day: Byte;
end;
var
BirthDay: Date;
begin
BirthDay.Year := 1997;
BirthDay.Month := 2;
BirthDay.Day := 14;
~~~
可以用with語句改進后半部分代碼,如下:
~~~
begin
with BirthDay do
begin
Year := 1995;
Month := 2;
Day := 14;
end;
~~~
在Delphi程序中,這種方法能用于訪問控件和類變量。現在通過with語句訪問列表框的條目,我們重寫上面循環例子的最后部分:
~~~
procedure TForm1.WhileButtonClick(Sender: TObject);
var
I: Integer;
begin
with ListBox1.Items do
begin
Clear; // shortcut
Randomize;
I := 0;
while I < 1000 do
begin
I := I + Random (100);
// shortcut:
Add ('Random Number: ' + IntToStr (I));
end;
end;
end;
~~~
當你使用控件或類時,with語句通常能簡化你的代碼,尤其對嵌套域。例如,你要改變窗體畫筆的寬度和顏色,你可以寫代碼如下:
~~~
Form1.Canvas.Pen.Width := 2;
Form1.Canvas.Pen.Color := clRed;
~~~
但如果用With語句代碼會更簡單:
~~~
with Form1.Canvas.Pen do
begin
Width := 2;
Color := clRed;
end;
~~~
當編寫的代碼很復雜時,with語句會很有用,也可省去一些臨時變量。但是這樣做也有缺點,因為這樣將使代碼的可讀性變差,特別對有相似或相同屬性的對象。
更嚴重的是,使用with語句可能會在代碼中融入微妙的邏輯錯誤,甚至連編譯器都難以發現。例如:
~~~
with Button1 do
begin
Width := 200;
Caption := 'New Caption';
Color := clRed;
end;
~~~
這段代碼改變了按鈕的Caption 和 Width屬性,但也改變了窗體的Color屬性,而不是按鈕的顏色!其原因是 TButton 控件沒有Color屬性, 又由于執行的代碼是針對窗體對象的(我們正在寫窗體的方法),所以窗體對象即成為默認的訪問對象。如果這樣寫:
~~~
Button1.Width := 200;
Button1.Caption := 'New Caption';
Button1.Color := clRed; // error!
~~~
編譯器會給出一個錯誤。通常,由于with語句在當前的塊中定義了新的標識符,省略了原有的標識符,可能引起在同一塊內錯誤地訪問另一個標識符(就象上面的這段代碼)。即使存在種種缺陷,我還是建議你習慣于使用with語句,因為with語句確實是非常便利,并且有時也會使代碼更容易讀懂。
然而,你應該避免使用多個with語句,如:
~~~
with ListBox1, Button1 do...
~~~
這樣會使后面的代碼非常難讀,因為,對該塊中定義的每個屬性,你都要根據相應的屬性以及控件的次序,才能推出所訪問的控件。
注意:說到可讀性,要知道Pascal 沒有endif 或endcase 語句。如果if語句有一個begin-end 塊,那么end標志語句結束;另外,case語句也總是以一個end結束。所有這些end語句,常常是一個接一個,使代碼難以理解,只有通過縮排跟蹤,才能追出一個end所對應的語句。解決這個問題的一個通用辦法, 也是使代碼更可讀的辦法,是在end后面加注釋,如下例:
~~~
if ... then
...
end; // if
~~~
* * * * *
### 結束語
本章描述了怎樣編寫條件語句和循環語句的代碼。程序通常被分成例程、過程或函數,而不是把所有語句列成長長的列表。這是下一章的主題,下一章也將介紹一些Pascal的高級內容。