精華區beta mud_sanc 關於我們 聯絡資訊
基礎 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.