注册 登录 查询

迷你方式显示论坛 RSS订阅此版新信息  
首页 >> 论坛 >> ┈┋MUD 交流区┋┈ >> 梦幻泥潭 >> 查看帖子
 新帖 新投票 讨论区 精华区 上篇 刷新 平板 下篇


 帖子主题: LPC教学手册
 
头衔 小混混

离线

ivy
门派 秋林拾叶
职务 总舵主
人物等级 炉火纯青
江湖威望 +8
江湖阅历 30
门派贡献 1507
实战经验 22561
文章 534
注册 05-01-09 22:36
发表 2007-04-12 17:49:51 人气:343

LPC的变数处理

6.1 回顾

现在你应该能利用你 mud 的标準物件库, 撰写一些简单的物件. 继承能让你使
用那些物件中已经定义好的函式, 而不用自己去定义. 另外, 你应该知道如何宣
告你自己的函式. 这一章将教你 LPC 的基本元素, 让你能藉由处理变数来定义
你自己的函式.

6.2 数值与物件

基本上, mud 裡头的物件都不一样的原因有两个:

1) 有的物件拥有不同的函式
2) 所有的物件都有不同的数值

现在, 所有的玩家物件都有同样的函式. 它们不一样的地方在於它们自己所拥有
的数值不同. 举例来说, 名字叫做「Forlock」的玩家跟「Descartes」「至少」
他们各自的 true_name 变数值不同, 一个是 "descartes", 另一个是 "forlock".

所以, 游戏中的改变伴随著游戏中物件值的改变. 函式名称就是用来处理变数的
过程名称. 例如说, create() 函式就是特别用来初始化一个物件的过程. 函式
之中, 有些特别的事称為指令. 指令就是负责处理变数的.

6.3 区域 (local) 和全域 (global) 变数

跟大多数程式设计语言的变数一样, LPC 变数可以宣告為一个特定函式的「区域
」变数, 或是所有函式可以使用的「全域」变数. 区域变数宣告在使用它们的函
式之内. 其他函式并不知道它们存在, 因為这些值只有在那个函式执行时才储存
在记忆体中. 物件码宣告全域变数之后, 则让后面所有的函式都能使用它. 因為
只要物件存在, 全域变数就会佔据记忆体. 你只有在整个物件中都需要某个值的
时候, 才要用全域变数. 看看下面两段程式码:

-----
int x;

int query_x() { return x; }

void set_x(int y) { x = y; }
-----

-----
void set_x(int y) {
int x;

x = y;
write("x 设定為 "+x+" 并且会消失无踪.\n");
}
-----

第一个例子裡, x 宣告在所有的函式之外, 所以在 x 宣告之后的所有函式都能
使用它. x 在此是全域变数.

第二个例子中, x 宣告在 set_x() 函式裡. 它只有在 set_x() 执行的时候存
在. 之后, 它会消失. 在此, x 是区域变数.

6.4 处理变数的值

给 driver 的指令 (instruction) 用来处理变数值. 一个指令的范例是:

-----
x = 5;
-----

上面的指令很清楚. 它把 5 这个数值指定给 x 变数. 不过, 这个指令牵涉到
一些对普通指令来说很重要的观念. 第一个观念是运算式 (expression).
一个运算式就是有值的一系列符号. 在上面的指令中, 运算式 5 的值指定给变
数 x. 常数 (constant) 是最简单的运算式. 一个常数就是不变的值, 像是整数
5 或是字串 "hello". 最后一个观念就是运算子 (operator). 在上面的例子
中, 使用了 = 这个指定运算 (assignment operator).

在 LPC 有更多其他的运算子, 还有更复杂的运算式. 如果我们进入一个更复杂
的层次, 我们得到:

-----
y = 5;
x = y +2;
-----

第一个指令使用指定运算子以指定常数运算式 5 的值给变数 y. 第二个指令把
(y+2) 的值以加法运算子把 y 和常数运算式 2 加起来, 再用指定运算子指
定给 x. 听起来一点意义都没有吧 ?

换另一种方法来讲, 使用多个运算子可以组成复杂的运算式. 在前面的范例中,
一个指令 x = y + 2; 裡面含有两个运算式:
1) 运算式 y+2
2) 运算式 x = y + 2
前面曾提过, 所有的运算是都有其值. 运算式 y+2 的值是 y 和 2 的总和
(在此是 7) ; 运算式 x = y + 2 「也」有其值 ── 7.
所以运算子有两个重要的工作:
1) 它们「可以」像函式一样当作输入.
2) 它们运算起来就像本身有值一样.
现在, 不是所有的运算子的功能都像 1) 一样. = 运算子将它右边的值指定给 x.
但是 + 就没有这种功能. 而且, 它们两个也有自己的值.

6.5 复杂的运算式

前面你大概已经注意到, 运算式 x = 5 「本身」也有个值是 5. 实际上, 因為
LPC 运算子如同运算式一样也有自己的值, 它们能让你写出一些非常难解、看起
来毫无意义的东西, 像是:
i = ( (x=sizeof(tmp=users())) ? --x : sizeof(tmp=children("/std/monster"))-1)
基本上只是说:
把外部函式 users() 传回的阵列指定给 tmp, 然后把此阵列元素的数目指
定给 x. 如果指定给 x 的运算式值為真 (不是 0) , 就指定 x 為 1 并
指定 i 的值為 x-1 的值. 如果 x 為偽, 则设定 tmp 為外部函式
children() 传回的阵列, 并指定 i 為阵列 tmp 的元素数目再减 1.
你曾经用过以上的叙述吗 ? 我很怀疑. 不过你可能看过或使用与它相似的运算
式, 因為一次合併这麼多的东西在一行裡面, 能提昇你程式码的执行速度. 比较
常使用 LPC 运算子这种特性的写法大概像这样:
x = sizeof(tmp = users());
while(i--) write((string)tmp[i]->query_name()+"\n");
取代这样子的写法:
tmp = users();
x = sizeof(tmp);
for(i=0; i<x; i++) write((string)tmp[i]->query_name()+"\n");
像是 for()、while() 、阵列......等等东西稍后会解释.
不过第一段程式码比较简洁, 执行起来也比较快.

附註: 在本章总结之后会对所有的 LPC 运算子有更详细的说明.

6.6 本章总结

你目前知道如何宣告变数, 并了解宣告、使用全域和区域变数之间的不同. 一旦
你熟悉你 driver 的外部函式, 你就能用许多不同的方法显示那些值. 另外, 藉
由 LPC 运算子, 你知道怎麼改变并运算变数裡头的值. 这当然对你很有用, 因
為它让你能做一些事, 像是算出从树上摘下了多少颗苹果, 一旦苹果都摘完了,
就没有人有苹果可摘. 很不幸, 你现在只会写寥寥几行能执行的程式. 换句话说
, 到下一章以前先别管苹果的问题, 因為你还不知道如何检查全部摘下的苹果数
目和树上原先的苹果数目是否相等. 你也不知道特殊的函式 init(), 能让你给
玩家使用新的指令. 但是你已经準备好撰写良好而复杂的区域程式码.

6.7 LPC 运算子

这一段将详细列出比较简单的 LPC 运算子, 包括对它们使用的值所作的事 (如
果有值的话), 以及它们自己拥有的值.

在此说明的运算子有:
= + - * / % += -= *= /= %=
-- ++ == != > < >= <= ! && ||
-> ? :

下面, 这些运算子将全部用相当简单的方式说明之, 但是你最好把每个运算子至
少都看过一次, 因為有些运算子的功能「不见得」如你所想的一样. 不过, 这段
说明可以当作相当好的一个参考.

= 指定运算子 (assignment operator):
范例: x = 5;
值: 在完成它的功能之后, 「左边」的变数值
说明: 把它「右边」任何运算式的值指定给它「左边」的变数. 注意, 你只
能於左边使用一个变数, 也不能指定给常数或复杂的运算式.

+ 加法运算子 (addition operator):
范例: x + 7
值: 左边值加上右边值的总和
说明: 把右边运算式的值加上左边运算式的值. 对整数 (int) 型态值来说
, 就表示数值总和. 对字串 (string) 来说, 表示右边的值接在左边
的值后面 ("a"+"b" 的值是 "ab"). 这个运算子不改变任何原始值 (
即变数 x 维持原来的值).

- 减法运算子 (subtraction operator):
范例: x - 7
值: 左边运算式的值减去右边的
解释: 除了它是减法以外, 与加法的特性相同.
字串: "ab" - "b" 的值是 "a".

* 乘法运算子 (multiplication operator):
范例: x*7
值与说明: 除了这个作数学乘法之外, 与加法、减法相同.

/ 除法运算子 (division operator):
范例: x/7
值与说明: 同上

+= 加法指定运算子(additive assignment operator):
范例: x += 5
值: 与 x + 5 相同
说明: 它把左边的变数值和右边的运算式值加起来, 把总和指定给左边的变
数.
例如: 如果 x = 2... x += 5 指定 7 值给变数 x. 整个运算式的值是 7.

-= 减法指定运算子 (subtraction assignment operator):
范例: x-=7
值: 左边的值减去右边的值.
说明: 除了减法以外, 与 += 相同.

*= 乘法指定运算子 (multiplicative assignment operator):
范例: x *= 7
值: 左边的值乘上右边的.
说明: 除了乘法以外, 与 -= 和 += 相似.

/= 除法指定运算子 (division assignment operator):
范例: x /= 7
值: 左边变数的值除以右边的值.
说明: 除了除法以外, 同上.

++ 后/前增加运算子 (post/pre-increment operators):
范例: i++ 或 ++i
值:
i++ 的值是 i
++i 的值是 i+1
说明: ++ 改变 i 的值, 将 i 加上 1. 但是, 运算式本身的值是多少,
要看你把 ++ 摆在哪裡. ++i 是前增加运算子. 这表示它的增加在给
予值「之前」. i++ 是后增加运算子. 它计算在 i 增加之前. 重点在
哪 ? 好, 目前这对你来说无关紧要, 但是你应该记住它代表的意思.

-- 后/前减少运算子 (post/pre-decrement operators):
范例: i-- 或 --i
值:
i-- 的值是 i
--i 的值是 i 减掉 1
说明: 除了是减法以外, 就像 ++

== 相等运算子 (equality operator):
范例: x == 5
值: 真或偽 (非 0 或 0)
说明: 它不更改任何值, 但是
如果两个值相等就传回真.
如果两边不相等则传回偽.

!= 不等运算子 (inequality operator):
范例: x != 5
值: 真或偽
说明: 如果左边的运算式不等於右边的运算式就传回真. 如果它们相等则传
回偽.

> 大於运算子 (greater than operator):
范例: x > 5
值: 真或偽
说明: 只有在 x 大於 5 时為真
如果相等或小於就為偽

< 小於运算子 (less than operator)
>= 大於或等於运算子 (greater than or equal to operator)
<= 小於或等於运算子 (less than or equal to operator):
范例: x < y x >= y x <= y
值: 真或偽
说明: 与 > 相似, 除了
< 如果左边小於右边就為真
>= 如果左边大於「或等於」右边则為真
<= 如果左边小於「或等於」右边就為真

&& 逻辑与运算子 (logical and operator)
|| 逻辑或运算子 (logical or operator):
范例: x && y x || y
值: 真或偽
说明: 如果右边的值和左边的值是非零值, && 為真.
如果任何一边是偽, 则 && 為偽.
对 || 来说, 只要两边任何一个值是真, 则為真. 只有两边都是偽值
时, 才為偽.

! 否定运算子 (negation operator)
范例: !x
值: 真或偽
说明: 如果 x 為真, 则 !x 為偽
如果 x 為偽, !x 就為真.

底下有两个更复杂的运算子, 在此為了存在而存在. 如果它们让你一头雾水也别
掛心.

-> 呼叫运算子 (the call other operator)
范例: this_player()->query_name()
值: 被呼叫函式的传回值
说明: 它呼叫右边这个函式, 而这个函式位於运算子左边的物件之内. 左边
的运算式「必须」是一个物件, 而右边的运算式「必须」是函式的名
字. 如果物件之中没有这个函式, 它会传回 0 (更精确一点, 没有定
义 (undefined) ).

? : 条件运算子 (conditional operator)
范例: x ? y : z
值: 上面的例子裡, 如果 x 為真, 其值為 y
如果 x 為偽, 其值為运算式 z
说明: 如果最左边的值為真, 这整个运算式的值就是中间的运算式. 不然,
就把整个运算式的值定為最右边的运算式.

相等 (equality) 的註解:

大家所犯的一种很难除错、很糟糕的错误是把该写 == 的地方写成 =. 因為运算
子有它的传回值, 这两种情况都能进行计算. 换句话讲, 这情形不会產生错误讯
息. 但是这两者的值大不相同. 例如:
if(x == 5) if(x = 5)
如果 x 是 5, 则其值為真. 反之则否.
x = 5 的值為 5 (所以它永远為真).
if 叙述会判断 () 之中的运算式是真还是偽, 所以如果你把 = 错当成 == ,
你就会得到永远為真的运算式. 你会扯掉许多根头髮, 也搞不清楚到底是為什麼
出错 :)


点击这里给我发消息
相关帖子
LPC教学手册 (ivy,5158,2007-04-12 17:41:06)
    LPC基本简介 (ivy,769,2007-04-12 17:43:36)
    LPC的资料型态 (ivy,496,2007-04-12 17:45:51)
    LPC的函式 (ivy,571,2007-04-12 17:47:08)
    LPC的基础继承 (ivy,406,2007-04-12 17:48:56)
    LPC的变数处理 (ivy,343,2007-04-12 17:49:51)
    LPC的流程控制 (ivy,413,2007-04-12 17:52:24)
    LPC的物件资料型态 (ivy,542,2007-04-12 17:53:16)
    好复杂啊。。。。 (gfdsa1,316,2007-05-20 04:00:57)