基礎 LPC

作者: Descartes of Borg
第一版: 23 april 1993
第二版: 22 june 1993

第四章: 函式 (functions)

4.1 回顧

現在, 你應該了解 LPC 物件由許多處理變數的函式所組成. 函式執行時就處理變數, 而經由「呼叫」執行這些函式. 在一個檔案裡, 函式之間的前後順序是無關緊要的. 變數在函式裡面被處理, 變數儲存在電腦的記憶體中, 而電腦把它們當作 0  與 1  來處理. 利用定義資料型態這種方法, 這些 0 與 1 被轉換成可使用的輸出及輸入結果. 字串 (string) 資料型態告訴 driver , 讓你看到或你輸入的資料應該是許多字元及數字的形式. 整數 (int) 型態的變數對你來說就是整數值. 狀況 (status) 型態對你來說就是 1 或 0. 無 (void) 資料型態對你或對機器而言都沒有值, 並不是用於變數上的資料型態.

4.2 什麼是函式 ?

就像數學函式, LPC 函式獲得輸入值, 然後傳回輸出值. 像 Pascal 語言把程序(procedure) 和函式 (function) 區分開來. 但是 LPC 不這樣做, 而知道這種區分也是有用的. Pascal 稱為程序的東西, 在 LPC 就是無傳回值 (void) 型態的函式. 也就是說, 程序或無傳回值函式沒有傳回輸出值. Pascal 稱為函式的東西, 就是有傳回輸出值的. 在 LPC 裡, 最短的正確函式是:

-----

void do_nothing() { }
-----

這個函式不接受輸入, 沒有任何指令, 也不傳回任何值.

要寫出正確的 LPC  函式有三個部分:

1)  宣告 (declaration)
2)  定義 (definition)
3)  呼叫 (call)
就像變數一樣, 函式也要宣告. 這樣一來, 讓 driver 知道: 1) 函式輸出的資料是什麼型態 2) 有多少個輸入的資料以及它們的型態為何. 比較普通的講法稱這些輸入為參數 (parameter). 所以, 宣告一個函式的格式如下:
傳回值型態  函式名稱 (參數 1, 參數 2, ...,  參數 N);
底下宣告一個 drink_water()  的函式, 它接受一個字串輸入, 而輸出一個整數:

-----

int drink_water(string str);
-----

str 是輸入的變數名稱, 會用於函式之中.

函式定義是描述函式實際上如何處理輸入值的程式碼.

呼叫則是其他函式之中, 呼叫並執行此函式的地方. 對 write_vals() 和 add() 兩個函式來說, 你可能會有這些程式碼:

-----

/*  首先, 是函式宣告. 它們通常出現在物件碼的開頭.
*/
void write_vals();
int add(int x, int y);

/*  接著是定義 write_vals() 函式. 我們假設這函式將會在物件以外被呼叫.
*/
void write_vals() {

int x;

/*  現在我們指定 x  為呼叫 add()  的輸出值. */
x = add(2, 2);
write(x+"\n");

}

/*  最後, 定義 add() */
int add(int x, int y) {

return (x + y);
}
-----

請記得, 哪一個函式定義在前都沒有關係. 這是因為函式並不是由前往後連續執行的. 函式只有被呼叫時才會執行. 唯一的要求是, 一個函式的宣告必須出現在函式的定義之前, 而且也必須在任何函式定義呼叫它之前.


4.3 外部函式 (efuns)

也許你已經聽過有人提過外部函式. 它們是外部定義的函式. 跟名稱一樣, 它們由 mud driver 所定義. 如果你已經撰寫 LPC 程式碼很久, 你大概已經發現你聽到的一些式子, 像是 this_player(), write(), say(), this_object()... 等等, 看起來很像函式. 這是因為它們是外部函式. 外部函式的價值在於它們比LPC 函式要快得多, 因為它們早已經以電腦了解的二進位格式存在著.

在前面的 write_vals() 函式裡, 呼叫了兩個函式. 第一個是 add() 函式, 是你宣告及定義的函式. 第二個, 則是稱做 write() 的外部函式. driver 早就幫你宣告並定義這個函式. 你只需要呼叫它.

創造外部函式是為了處理普通的、每天都用得到的函式呼叫、處理 internet socket 的輸出與輸入、其他用 LPC 難以處理的事. 它們是在 game driver 內以 C 寫成的, 並與 driver 一起編譯在 mud 開始之前, 讓它們執行起來快得多. 但是對你來說, 外部函式呼叫就像對你的函式呼叫一樣. 不過, 任何外部函式還是要知道兩件重要的事: 1) 它的傳回值是什麼, 2) 它要什麼參數.

外部函式的詳細資料, 像是輸入參數和傳回值, 常常可以在你的 mud 中的 /doc/efun 目錄找到. 我沒有辦法在這裡詳細介紹外部函式, 因為每種 driver 的外部函式都不相同. 但是, 你常常可以藉由「man」或「help」指令 (視 mudlib 而定) 找到詳細的資料. 例如指令「man write」會給你 write 外部函式的詳細資料. 如果都不行, 「more /doc/efun/write」也可以.

看過 write  的詳細資料之後, 你應該找到 write 是宣告成這樣:

-----

void write(string);
-----

這樣告訴你, 要正確呼叫 write  不應該期待它有傳回值, 而且要傳入一個字串型態的參數.


4.4 定義你自己的函式

雖然在檔案中, 你的函式次序誰先誰後都沒有關係, 但是定義一個函式的程式碼的先後順序就非常重要. 當一個函式被呼叫時, 函式定義中的程式碼按照出現的先後順序執行. 先前的 write_vals() 中, 這個指令:
 
-----
x = add(2, 2);
-----

如果你想看到 write() 使用正確的 x 值, 就必須把它放在 write() 呼叫之前.

當函式要傳回一個值時, 由「return」指令之後跟著與函式相同資料型態的值所完成. 在先前的 add()  之中, 指令「return (x+y);」把 (x+y)  的值傳回給write_vals()  並指定給 x. 在更普通的層次上來說, 「return」停止執行函式, 並傳回程式碼執行的結果給呼叫此函式的函式. 另外, 它將跟在它後面任何式子的值傳回呼叫的函式. 要停止執行失去控制的無傳回值函式, 使用 return; 而後面不用加上任何東西. 請再次記得, 使用「return」傳回任何式子的資料型
態「必須」與函式本身的資料型態相符合.


4.5 本章總結

定義 LPC  物件的檔案是由函式所組成的. 函式依次由三個部分組成:
1)  宣告
2)  定義
3)  呼叫
函式宣告通常出現在檔案的最前面, 在任何定義之前. 不過函式只要求在函式定義之前以及任何函式呼叫它之前宣告它. 函式定義可以任何順序出現在檔案裡, 只要它們都放在宣告之後. 另外, 你不可以再一個函式裡面定義另一個函式.
函式呼叫則出現在其他任何函式中, 任何程式碼想執行你的函式的地方. 呼叫也可以出現在自己的函式定義中, 但是這種做法並不建議給新手去做, 因為它很容易變成無窮迴圈.

函式定義依序由底下的部分所組成:

1)  函式傳回值型態
2)  函式名稱
3)  一個左小括號 (  接著列出參數再加上一個右小括號 )
4)  一個左大括號 {  指示 driver 從這裡開始執行
5)  宣告只用在這個函式中的任何變數
6)  指令、式子、視需要呼叫其他函式
7)  一個右大括號 }  描述函式碼在此結束. 對於無傳回值函式來說, 如果在此還沒有碰到「return」指令 (只適用於無傳回值函式) , 會如同有碰到「return」指令一樣回到原來呼叫的函式執行.
最短的函式是:

-----

void do_nothing() {}
-----

因為這個函式不接受任何輸入, 不做任何事, 也不傳回任何輸出.

任何無傳回值型態以外的函式「必須」傳回一個與函式資料型態相同的值.

每一種 driver 都有一套早已經幫你定義好的函式, 它們叫做外部函式. 你不需要宣告或定義它們, 因為它們早已經幫你做好這些事. 更深入一點, 執行這些函式比起執行你的函式要快得多, 因為外部函式是 driver 的一部份. 再者, 每一個 mudlib 都有特殊函式像是外部函式一樣, 早已經為你宣告並定義好. 但是不同的是, 它們用 LPC  定義在 mudlib 裡面. 它們叫做模擬外部函式 (simul_efuns, 或 simulated efuns). 在大多數的 mud  裡, 你可以在 /doc/efun 目錄底下找到關於它們的詳細資料. 另外, 很多 mud 有稱作「man 」或「help」的命令, 讓你可以方便地叫出這些資料檔案.

程式風格的註解:

有些 driver 可能不會要求你宣告函式, 有些不會要求你指定函式的傳回值型態. 無論如何, 底下有兩個理由勸你不要省略以上這些動作:


翻譯: Spock of Final Frontier  98.Jan.25.

回上一頁