1. 程式人生 > >Delphi 之 第五課 流程語句

Delphi 之 第五課 流程語句

 

  首先,什麼是語句?為什麼需要語句?語句又是怎麼組成的?語句簡單的說就是一個分號結尾的句子,我們可以稱為這是一條語句。那這條句子就是由一些關鍵字和操作指令組成。語句通常放在過程和函式中,一個程式通常都是由若干個語句組成,沒有語句,當然程式也就無法執行。

語句從大的範圍可以分為簡單語句和複合語句。

簡單語句和複合語句

  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語言中都有,它們包括 forwhilerepeat 語句。如果你用過其他程式語言,你會發現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 面板中的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按鈕後顯示的結果 

注意:用 BreakContinue 系統過程可以改變迴圈執行的標準流程。Break 中斷迴圈;Continue直接跳至迴圈測試句,或使計數器增加一個步長,然後繼續迴圈(除非條件為空或計數器達到最大值)。還有兩個系統過程 ExitHalt,讓你立即從函式或過程中返回,或者終止程式。

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的高階內容。

下一頁: 過程與函式