作者: Descartes of Borg
藉由 =、+=、-=、++、-- 等运算式, 可以指定或更改变数的值. 这些运算式可以与 -、+ 、* 、/ 、% 结合使用. 但是, 到目前为止, 我们只告诉你如何用函式, 以线性的方式写出这些. 例如:
int hello(int x) {
x--;
write("嗨, x 是 "+x+".\n");
return x;
}
你应该知道怎麽写出这个函式并了解它. 不过, 如果你只想於 x = 1 时显示 x 的值怎麽办 ? 不然, 如果你想在传回 x 之前, 一直显示出 x 的值直到 x = 1 又要怎麽做 ? LPC 使用的流程控制与 C 和 C++ 并无二致.
if(运算式) 指令;我们讨论这些东西之前, 先谈一下什麽是运算式和指令. 运算式是任何有值的东西, 像是变数、比较式 (像 x > 5, 如果 x 是 6 或 6 以上, 则其值为 1, 不然其值为 0)、指定式 (像 x += 2). 而指令是任何一行单独的 LPC 码, 像是函式呼叫、值指定式 (value assignment)、值修改式 (value modification) ......等等.if(运算式) 指令;
else 指令;if(运算式) 指令;
else if(运算式) 指令;
else 指令while(运算式) 指令;
do { 指令; } while(运算式);
switch(运算式) {
case (运算式): 指令; break;
default: 指令;
}你也应该知道 && 、||、==、!=、! 这些运算子. 它们是逻辑运算子. 当条件为真时, 它们传回非零值, 为伪时则传回 0. 底下是运算式值的列表:
(1 && 1) 值: 1 (1 和 1)使用 && 的运算式中, 如果要比较的第一项测试值为 0, 则第二项永远不会测试之. 使用 || 时, 如果第一项为真 (1), 就不会测试第二项.
(1 && 0) 值: 0 (1 和 0)
(1 || 0) 值: 1 (1 或 0)
(1 == 1) 值: 1 (1 等於 1)
(1 != 1) 值: 0 (1 不等於 1)
(!1) 值: 0 ( 非 1)
(!0) 值: 1 ( 非 0)
我们介绍第一个改变流程控制的运算式是 if(). 仔细看看底下的例子:
1 void reset() {
如果你想执行一个以上的指令, 你必须按照以下的方法来做:
2 int x;
3
4 ::reset();
5 x = random(100);
6 if(x > 50) set_search_func("floorboards", "search_floor");
7 }
- 每一行的编号仅供参考.
- 在第二行, 我们宣告一个称为 x 的整数型态变数. 第叁行则优雅地留下一行空白, 以明示宣告结束和函式码开始的界线. 变数 x 只能在 reset() 函式中使用.
- 第四行呼叫 room.c 中的 reset().
- 第五行使用 driver 外部函式的 random() 以传回一个随机数字, 此数字介於 0 到参数减一. 所以在此我们想得到一个介於 0 到 99 的数字.
- 第六行中, 我们测试运算式 (x>50) 的值, 看它是真是伪. 如果为真, 则呼叫 room.c 的函式 set_search_func(). 如果为伪, 就不可能执行呼叫 set_search_func() .
- 第七行, 函式将 driver 的控制权交回呼叫此函式的函式 (在这个例子中, 呼叫 reset() 的是 driver 自己) , 也没有传回任何值.
if(x>50) {
set_search_func("floorboards", "search_floor");
if(!present("beggar", this_object())) make_beggar();
}注意运算式为真时, 要执行的指令以 {} 包围起来. 这个例子里, 我们再次呼叫 room.c 中的 set_search_func() 来设定一个函式 (search_floor()) , 这个函式稍後被你设定为: 玩家输入 "search floorboards" 时, 呼叫 search_floor(). (注: 这种例子要看 mudlib 而定. Nightmare 有这个函式呼叫, 其他 mudlib 可能会有类似的东西, 也可能完全没有这一类用途的函式) 接着, 另一个 if() 运算式检查 (!present("beggar", this_object())) 运算式是否为真. 测试运算式中的 ! 改变它後面运算式的真伪. 在此, 它改变外部函式 present() 的真伪值. 在此, 如果房间里有个乞丐, present() 就传回乞丐这个物件 (this_object()), 如果没有乞丐, 则传回 0. 所以, 如果房间里面还有个活乞丐, (present("beggar", this_object())) 的值就会等於乞丐物件 (物件资料型态) , 不然它会传回 0. ! 会把 0 变成 1 , 把任何非零值 (像是乞丐物件) 变成 0. 所以, 房间里没有乞丐时, 运算式 (!present("beggar", this_object())) 为真, 反之, 有乞丐为 0. 如果房间里没乞丐, 它呼叫你房间码中定义的函式来制造一个新的乞丐, 并放进房间. (如果房间中已经有一个乞丐, 我们不想多加一个 :) )
当然, if() 常常和一些条件一起出现 :). LPC 里, if() 叙述的正式写法为:
if(运算式) { 一堆指令 }这样表示:
else if(运算式) { 一堆指令 }
else { 一堆指令 }
如果运算式为真, 执行这些指令.
不然, 如果第二个运算式为真, 执行第二堆指令.
如果以上皆伪, 执行最後一堆指令.
你可以只用 if() :if(x>5) write("Foo,\n");跟着一个 else if():if(x > 5) write("X 大於 5.\n");跟着 else:
else if(x >2) write("X 小於 6, 大於 2.\n");if(x>5) write("X 大於 5.\n");或是把上面列出来的东西全写出来. 你有几个 else if() 都没关系, 但是你必须有一个 if() (也只能有一个), 也不能有一个以上的 else . 当然, 上面那个乞丐的例子中, 你可以在 if() 叙述中重复使用 if() 指令. 举例来说,
else write("X 小於 6.\n");if(x>5) {
if(x==7) write("幸运数字 !\n");
else write("再试一次.\n");
}
else write("你输了.\n");
原型:while(运算式) { 一堆指令 }这两者让你在运算式为真时, 一直重复执行一套指令. 假设你想设定一个变数等於玩家的等级, 并持续减去随机的金钱数量或可承受伤害值 (hp, hit points) 直到该等级变数为 0 (这样一来, 高等级的玩家失去的较多). 你可能会这样做:
do { 一堆指令 } while(运算式);
1 int x;
2
3 x = (int)this_player()->query_level(); /* 这行内容等一下会解释 */
4 while(x > 0) {
5 if(random(2)) this_player()->add_money("silver", -random(50));
6 else this_player()->add_hp(-(random(10));
7 x--;
8 }
第叁行中呼叫的 this_player()->query_level() 运算式 (译注: 之後内容遗失, 在此由译者补充) 的意义: 呼叫 this_player() 外部函式, this_player() 传回一个物件, 为正在呼叫此函式的玩家物件. 再呼叫此玩家物件中的 query_level() 函式. (译注: 补充结束)在第四行, 我们开始一个回圈, 只要 x 大於 0 就重复执行.
我们可以用另一种写法:while(x) {(译注: 以下遗失, 由译者补充)
由於 x 本身稍後会一直减 1 直到到 x = 0 , 所以 x = 0 时也是伪值 (为 0).
第五行以 random(2) 随机传回 0 或 1. 如果它传回 1 (为真), (译注: 补充完毕) 则呼叫玩家物件的 add_money() 将玩家身上的银币随机减少 0 到 49 枚.
在第六行, 如果 random(2) 传回 0, 我们呼叫玩家物件中的 add_hp() 函式来减少 0 到 9 点的可承受伤害.
第七行里, 我们把 x 减 1.
第八行执行到 while() 指令的终点, 就回到第四行看 x 是否还大於 0 . 此回圈会一直持续执行到 x 小於 1 才结束.但是, 你也许想在你执行一些指令「之後」再测试一个运算式. 比如用上面的例子, 如果你想让每个人至少执行到一次指令, 甚至还不到测试的等级:
int x;这个例子真的很奇怪, 因为没几个 mud 会有等级为 0 的玩家. 而且, 你可以修改前面例子中的测试条件做到同样的事. 不管如何, 这个例子只是要展现出do {} while() 的如何工作. 如你所见, 此处在回圈开始的时候没有初始条件 (在此不管 x 的值为何, 立刻执行) , 回圈执行完之後才测试. 这样能保证回圈中的指令至少会执行一次, 无论 x 为何.
x = (int)this_player()->query_level();
do {
if(random(2)) this_player()->add_money("silver", -random(50));
else this_player()->add_hp(-random(10));
x--;
} while(x > 0);
原型:for(初始值 ; 测试运算式 ; 指令) { 指令 }初始值:让你设定一些变数开始的值, 用於回圈之内. 此处可有可无.测试运算式:与 if() 和 while() 的运算式相同. 当这一个 (或一些) 运算式为真时, 执行回圈. 你一定要有测试运算式.指令:一个 (或一些) 运算式, 於每个回圈执行完毕之後执行一次. 此处可有可无.注:for(;运算式;) {}与while(expression) {}「 完 全 相 同 」范例:
1 int x;这个 for() 回圈与前面 while() 的例子「完全相同」. 还有, 如果你想初始化两个变数:
2
3 for(x= (int)this_player()->query_level(); x>0; x--) {
4 if(random(2)) this_player()->add_money("silver", -random(50));
5 else this_player()->add_hp(-random(10));
6 }for(x=0, y=random(20); x<y; x++) { write(x+"\n"); }在此, 我们初始化 x 和 y 两个变数, 我们把它们用逗号分开来. 你可以在 for() 叁个部分的运算式中如此使用.
原型:switch(运算式) {这样有点像 if() 运算式, 而且对 CPU 也好得多, 但是 switch() 很少有人使用它, 因为它看起来实在很复杂. 但是它并非如此.
case 常数: 一些指令
case 常数: 一些指令
......
case 常数: 一些指令
default: 一些指令
}第一点, 运算式不是测试条件. case 才是测试. 用普通的话来读:
1 int x;就是:
2
3 x = random(5);
4 switch(x) {
5 case 1: write("X is 1.\n");
6 case 2: x++;
7 default: x--;
8 }
9 write(x+"\n");
设定变数 x 为一个 0 到 4 的随机数字.
x = 1 的 case 中, 显示 x 的值, 将 x 加上 1 之後再将 x 减 1.
x = 2 的 case 中, 将 x 加上 1 之後再减 1.
其他情形下, x 减 1.
显示 x 的值.
switch(x) 基本上告诉 driver, 变数 x 的值是我们想配合各个 case 的情形. 当 driver 找到一个能配合的 case 时, 这个 case 「以及所有在它之後」的 case 都会执行. 你可以使用 break 指令, 在执行一个 case 之後跳出 switch 叙述, 就像其他流程控制叙述一样. 稍後会解释这一点. 只要 switch() 流程还没中断, 任何 x 值都会执行 default 叙述. 你可以在 switch 叙述中使用任何资料型态:string name;对我来说, 我会看到:
name = (string)this_player()->query_name();
switch(name) {
case "descartes": write("You borg.\n");
case "flamme":
case "forlock":
case "shadowwolf": write("You are a Nightmare head arch.\n");
default: write("You exist.\n");
}You borg.Flamme、Forlock 、或 Shadowwolf 会看到:
You are a Nightmare head arch.
You exist.You are a Nightmare head arch.其他人会看到:
You exist.You exist.
以下的指令:return continue break
能改变前面提过的那些东西, 它们原本的流程.
首先,return
一个函式中, 不管它出现在哪里, 都会终止执行这个函式并将控制权交回呼叫这个函式的函式. 如果这个函式「不是」无传回值 (void) 的型态, 就必须在 return 叙述之後跟着一个传回值. 一个绝对值函式长得大概像这样:
int absolute_value(int x) {第二行里, 函式终止执行, 并回到呼叫它的函式. 因为在此, x 已经是正整数.
if(x>-1) return x;
else return -x;
}continue 在 for() 和 while() 叙述中用得最多. 它停止目前执行的回圈, 把回圈送回开头执行. 例如, 你想要避免除以 0 的情况:
x= 4;你会看到以下的输出:
while( x > -5) {
x--
if(!x) continue;
write((100/x)+"\n");
}
write("完毕.\n")33为了避免错误, 每一次回圈都检查 x, 确定 x 不为 0. 如果 x 是 0, 则回圈回到开头处的测试运算式, 并不终止目前的回圈.
50
100
-100
-50
-33
-25
完毕.
用 for() 运算式来说就是:for(x=3; x>-5; x--) {这样执行起来差不了多少. 注意, 这样子跟前面输出的结果一模一样. 当 x = 1, 它测试 x 是否为 0, 如果不是, 就显示 100/x, 然後回到第一行, 将 x 减 1, 再检查 x 是否是 0 , 如果为 0, 回到第一行并把 x 再减 1.
if(!x) continue;
write((100/x)+"\n");
}
write("完毕.\n");
break它停止执行流程控制叙述. 不管它出现在叙述里面的任何地方, 程式控制会结束回圈. 所以, 如果在上面的例子中, 我们把 continue 换成 break, 则输出的结果会变成像这样:
33continue 最常用於 for() 和 while() 叙述. 但是 break 常用於 switch().
50
100
完毕.switch(name) {下面这个函式跟上面的一样:
case "descartes": write("You are borg.\n"); break;
case "flamme": write("You are flamme.\n"); break;
case "forlock": write("You are forlock.\n"); break;
case "shadowwolf": write("You are shadowwolf.\n"); break;
default: write("You will be assimilated.\n");
}if(name == "descartes") write("You are borg.\n");但是 switch 叙述对 CPU 比较好.
else if(name == "flamme") write("You are flamme.\n");
else if(name == "forlock") write("You are forlock.\n");
else if(name == "shadowwolf") write("You are shadowwolf.\n");
else write("You will be assimilated.\n");
如果这些指令放在多层巢状 (nested) 的叙述中, 它们会改变最近的叙述.
这一章讲的东西实在是太多了, 但是它们马上就用得到. 你现在应该完全了解 if()、for()、while()、do{} while()、switch(), 也该完全了解如何使用 return、continue、break 改变它们的流程. 使用 switch() 要比一大堆 if() else if() 来得有效率, 所以应该尽量使用 switch() . 我们也向你介绍过怎麽呼叫其他物件中的函式. 不过, 以後会详细解释这个主题. 你现在应该能轻轻松松写出一个简单的房间 (如果你已经读过你 mudlib 有关建造房间的文件)、简单的怪物、其他简单的物件.