现在, 你应该了解 LPC 物件由许多处理变数的函式所组成. 函式执行时就处理变数, 而经由「呼叫」执行这些函式. 在一个档案里, 函式之间的前後顺序是无关紧要的. 变数在函式里面被处理, 变数储存在电脑的记忆体中, 而电脑把它们当作 0 与 1 来处理. 利用定义资料型态这种方法, 这些 0 与 1 被转换成可使用的输出及输入结果. 字串 (string) 资料型态告诉 driver , 让你看到或你输入的资料应该是许多字元及数字的形式. 整数 (int) 型态的变数对你来说就是整数值. 状况 (status) 型态对你来说就是 1 或 0. 无 (void) 资料型态对你或对机器而言都没有值, 并不是用於变数上的资料型态.
就像数学函式, LPC 函式获得输入值, 然後传回输出值. 像 Pascal 语言把程序(procedure) 和函式 (function) 区分开来. 但是 LPC 不这样做, 而知道这种区分也是有用的. Pascal 称为程序的东西, 在 LPC 就是无传回值 (void) 型态的函式. 也就是说, 程序或无传回值函式没有传回输出值. Pascal 称为函式的东西, 就是有传回输出值的. 在 LPC 里, 最短的正确函式是:-----
void do_nothing() { }-----这个函式不接受输入, 没有任何指令, 也不传回任何值.
要写出正确的 LPC 函式有叁个部分:
1) 宣告 (declaration)就像变数一样, 函式也要宣告. 这样一来, 让 driver 知道: 1) 函式输出的资料是什麽型态 2) 有多少个输入的资料以及它们的型态为何. 比较普通的讲法称这些输入为参数 (parameter). 所以, 宣告一个函式的格式如下:
2) 定义 (definition)
3) 呼叫 (call)传回值型态 函式名称 (参数 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);}请记得, 哪一个函式定义在前都没有关系. 这是因为函式并不是由前往後连续执行的. 函式只有被呼叫时才会执行. 唯一的要求是, 一个函式的宣告必须出现在函式的定义之前, 而且也必须在任何函式定义呼叫它之前.
也许你已经听过有人提过外部函式. 它们是外部定义的函式. 跟名称一样, 它们由 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 不应该期待它有传回值, 而且要传入一个字串型态的参数.
虽然在档案中, 你的函式次序谁先谁後都没有关系, 但是定义一个函式的程式码的先後顺序就非常重要. 当一个函式被呼叫时, 函式定义中的程式码按照出现的先後顺序执行. 先前的 write_vals() 中, 这个指令:
-----x = add(2, 2);-----如果你想看到 write() 使用正确的 x 值, 就必须把它放在 write() 呼叫之前.
当函式要传回一个值时, 由「return」指令之後跟着与函式相同资料型态的值所完成. 在先前的 add() 之中, 指令「return (x+y);」把 (x+y) 的值传回给write_vals() 并指定给 x. 在更普通的层次上来说, 「return」停止执行函式, 并传回程式码执行的结果给呼叫此函式的函式. 另外, 它将跟在它後面任何式子的值传回呼叫的函式. 要停止执行失去控制的无传回值函式, 使用 return; 而後面不用加上任何东西. 请再次记得, 使用「return」传回任何式子的资料型
态「必须」与函式本身的资料型态相符合.
定义 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) 大家认为这样子写程式是个好习惯.