基礎 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 可能不會要求你宣告函式, 有些不會要求你指定函式的傳回值型態.
無論如何, 底下有兩個理由勸你不要省略以上這些動作:
1) 對其他人來說 (還有你自己過了一段時間之後) , 會比較容易讀懂你的
程式碼並了解程式碼的意義. 這對除錯時特別有用, 有很多錯誤 (除了
放錯地方的各種括號) 發生在資料型態上 (有沒有碰過「Bad arg 1 to
foo() line 32」? (程式第三十二行, 呼叫 foo() 時的第二個參數有錯) ).
2) 大家認為這樣子寫程式是個好習慣.
翻譯:
Spock of Final Frontier 98.Jan.25.