LPC 基础教程

这一节将教给你关于编程的一些最基础的知识.了解下面这些内容的要点.
同时向你介绍物体导向编程的概念和一些关于mudlib的知识.

基础编程概念:最基础的编程概念.
基础LPC:初学者学习LPC
预处理:什么是预处理和他是如何工作的.

基础编程概念:

我们先从基础编程的原则,LPC结构和LPC的环境入手.

simul_efun.c  可以自动提供给游戏中所有的其它 objects,在这里可以找到
所有的模拟超越函数s_funs(simul_efuns).这些函数完整的并且是完全透明的
呈现给你.你可以像使用其它的超越函数(efun)一样使用他,可能都意识不到
他实际上只是模拟超越函数sfun.

LPC与C语言是非常相似的,尽管存在一些不同.一个老练的编程者会发现:
他基本上就是加入了一些方便易用的类型(types)和一套处理这些类型(types)的函数.

一. 注解:  如何在程序中加注解.
二. 数据类型:  LPC中的数据类型.
三. 变量的宣布:  如何宣布一个变量.
四. 函数的宣布:  如何宣布一个函数.
五. 语句/表达式:  语句和表达式的概念.
六. 范围和原型:  范围和函数原型的概念.
七. 操作表达式:  对操作表达式的解释.
八. 字首配置: 特殊的字首配置语句.
九. 条件:  条件语句的类型.
十. 优先级:  取值的优先级和规则.
十一. loop 语句:  在LPC中如何写 loops 语句.
十二. break/continue:  break 和 continue 语句.
十三. arrays 和 mappings:  对于特殊的 LPC 类型(types)的详解.

一.注解:
	大家也许很奇怪,为什么要以注解开始?注解在程序中是无处不在的,
所以我们一开始就应该学会去识别它们.

注解有两种类型:
// 这种注解一直延伸到这一行的末尾.
/* 这是一种封闭型的注解 */

第一种类型的注解是以"//"字符为开始,不管这一行一直到末尾都有什么,都会被认为
是注解而在编译的时候被跳过.如果你要用这种方法做多行的注解,那么每一行都要以
一个"//"字符为开始.

第二种注解是一种确定长度的注解.它以"/*"开始,以"*/"结束,这种注解非常适合于
写多行的注解.你只需在妮所写内容开始和末尾标上注释符就可以了.
不过要注意 "/*  */" 类型的注解是不允许嵌套的,例如:

/* 注解 A /* 注解嵌套 A  */ 第一个注解的延续 */

编译器会把找到的第一个"*/" 作为此注解的结尾,而此后的 "第一个注解的延续 */"
会被当作LPC码来翻译,当然,得到的只会是错误信息(error message).

二.数据类型:

	物件(object)将自己的各种信息保存在变量中,他们可以容纳各种信息,把
信息传给函数并返回各种类型的数据.

	按理说数据类型有一种就够了,但事实上最好的方法还是你能够区分不同类型
的信息.这看起来是增加了你的编程难度,实际上是减小了错误程序的风险和程序的可读
性,他极大的改善了程序的执行时间和调试程序的难度.

目前已定义的函数类型有:

"void" :  这种数据类型专门用于无传回值的函数的返回值.

"int" :  也就是整数类型,范围由 -2147483648 到 2147483647.

"float" : 浮点数,十进制的小数,范围由 1.17549435e-38 到 3.40282347e+38. 
	  这个范围会依据不同的 MUD 所运行的平台不同可能有一些差异.有一点
	  要注意的是 1. 和 .6789 并不会被识别为浮点数,你必须写全它的整数
	  部分和小数部分,尽管那部分可能是 0 .

"string" : 字符串.字符串是由一串可打印的字符组成.字符串可以包括特殊字符如
	   换行符"\n",许多LPC表达式都可以直接引用字符串,这使得字符串的应用
	   变得非常便利和容易.

"mapping" : 映射.映射是LPC的一个非常便利的创造(但他对内存的耗费是相当大的,
	    所以使用时要小心),映射简单的说是一个组合的值的序列,假定你要储存
	    一组人的年龄,如 jyt 90 ,uudd 95 ,dragon 99,在LPC里你可以这样写:
	    (["jyt" : 90, "uudd" : 95, "dragon" : 99]),你可以看到右边的值和
	    左边的值是一一对应的,这样你就可以通过简单的检索操作用左边的值作
	    为检索线索而找到与其对应的值.

"object" : 物件指标.简单的说就是一个物体,在MUD中如房间,盔甲,人物兵器等.可以
	   参考MUD中的一些现有的程序.

"function" : 函数指标.简单的说就是具有对一系列的传入值进行一定的操作,返回一系列
	     预定值的功能的程序段.请注意, 「传回值变数」的形态必须符合函数宣布时
	     的型态.函数的形态可以是数据类型中除此以外所有的形态.

"array" : 阵列.阵列种的元素可以是int, float, string, mapping, object,mixed,宣告时
	  用一个"*"放在变数前面来表示此为一个包含此类型变数的阵列,很多可以对阵列进
	  行操作的函数的存在使得对阵列的应用变得非常容易和快捷.

"mixed" : 混合指标.这种类型可以包括以上所有其它的类型.当对某项信息的类型无法预先确
	  定的时候就要用到这种类型.不过不要用在不是非用不可的地方,不然只会给你自己
	  带来麻烦.

三.变量的宣布:

	一个变量是一个由字母串标识出来的用来存放数据的信息储存区,这个信息储存区的名
字可以由以字母打头的字符串组成(字符串内字符的数量小于32),习惯上通常用于函数内部的所
有的变量名字都由小写字母组成,全局变量的名字的第一个字符用大写字母,其余用小写字母,特
殊字符除了用于分割词的"_"字符外其它都是不使用的.变量在使用的时候都是要给他一个名字的,
你可以这样来宣布它们,如:

int counter;
float height, weight;
mapping age_map;

变量必须在一个封闭程序块的开始处( 第一个"{"符号后其它的语句前 )宣布.全局变量即对程序中所
有函数都有效的变量,必须在此文件的开始处宣布.

程序执行变量宣布时,会把他们都初始化为 0,儿并不是它们的无(null)状态.例如宣布 mapping,
array,string , 他们并不会被初始化为 ([]),({}),"",而是都被初始化为0,所以按以下方法宣布
变量是一个好的编程习惯:

int counter = 8;
float height = 3.0, weight = 1.2;
mapping age_map = ([]);
object *monsters = ({});

四. 函数的宣布: 

	函数的宣布必须明确的给出这个函数返回值的数据类型.函数的命名规则和变量是一样的,
习惯上通常函数名字都由小写字母组成,特殊字符除了用于分割词的"_"字符外其它都是不使用的.
函数的名字应该能明确反映出函数的功能.以下是一个函数的例子:

/*
* Function name: do_plus
* Description: Compute int x plus int y.
* Variables: x and y 
* Returns: The sum.
*/

int do_plus(int x,int y)
{
	int sum;

	sum = x + y;
	write( x + "+" + y + "=" + sum +"\n");
	return sum;
}

一个没有传回值的函数要定义成 void 型,例如:

void write_to_all(string msg)
{
	users()->catch_msg(msg);
}

五. 语句/表达式:

 1. 语句
	语句就是有单个和多个表达式组成的完整的指令行(sentence of instructions),有时
由于指令行太长为了增加可读性而需要将它打断,一般的都是在指令行中的两个词之间将它打断,
但如果是在一个字符串中间需要打断,你就要在这一行的末尾加上一个"\"来告诉game driver要
去连接下一行的相应部分.例如:

write("This is an example of \
a broken string.\n");

由"\"打断一个语句是非常难看的同时也是程序码不容易阅读,通常我们都是在语句中两个字符串
相加(+)的部分打断它,例如:

write("This is a better way of " +
"breaking a string.\n");

LPC里的语句通常是以分号";"结束的,你可以接着输入下面的语句,一般为了可读性我们都是
换一行继续输入.

 2.表达式

	表达式就是一条指令或是一套指令而他们的运算结果是一个某种类型的值,例如"+",
它用两个其它的表达式计算出一个结果.一个变量也是一个表达式.一个函数呼叫是一个有效的
表达式.

 3,封闭语句

	这种情况很多,例如条件语句,它只在一个特定的条件下执行一个特定的语句,如果你
想在这个条件产生时执行几条语句而并不是一条怎么办呢?这就要用到封闭语句了,一个封闭的
语句是由"{"开始,由"}"结尾,在这中间你可以按你的需要放置很多各种类型的语句(包括变量
宣布语句),封闭语句并不以";"为结束符,但你非要放上一个也无所谓.

六. 范围和原型: (暂缺...............)


七. 操作表达式:

	LPC语言定义了大量的丰富的操作表达式,下面就对此做一个详细的介绍.
为了节省篇幅我们先做一个约定:

"E" : 代表一个表达式.
"V" : 代表一个变量.

*****普通表达式:

1. (E) 这里E的值将会被先计算出来以后再参与括号外的操作.

2. E1,E2 这里E1将会首先被定值并把结果储存起来,然后再把E2定值,结果将不予保存,
   最后E1所储存的结果会被作为整个表达式的结果返回.

   例如语句 a = 1, 2, 3; 结果会把赋值为 1.

3. V = E 把整个表达式 E 的值赋给变量 V . 例如:
   a = b = 4;  a 和 b 都被赋值为 4,也可以写成:
   a = (b = 4);

*****算数表达式:

1. E1 + E1  这个表达式的目的是把 E1 和 E2 的结果相加在一起,类型可以是 int,
   float, string, array, mapping, 其中类型的相加只是把后一个参数简单的连接
   到第一个参数的末尾.一般来说只有同种类型的参数可以相加,但你可以把 int 类型
   的参数加到 string 类型后,计算方法是把 int 转换成 string 加到前一个 string
   类型的参数后面,例如:  "string" + 5 的结果就是 "string5" .

2. E1 - E2 这个表达式的目的就是 E1 减去 E2, E1 和 E2 必须是同种类型的 int,
   float和各种类型的 array. 如果是 array 类型,如果E1包含E2的元素,就从E1里把
   E2 的元素去掉,如果不包含,阵列将不会被改变.

3. E1 * E2 这个表达式的目的是把 E1 的值乘上 E2 的值.只能是int和float类型能进
   行这种操作.

4. E1 / E2 这个表达式的目的是把 E1 的值除以 E2 的值,只能是int和float类型能进
   行这种操作.

5. E1 % E2 这个表达式返回的是表达式"E1/E2"的余数,只有 int 类型能进行这种操作.
   例如 14 % 3 的值是 2 ( 14 / 3 = 4, 14 - ( 3 * 4 ) = 2).

6. -E 这个表达式的目的是把E的正负状态颠倒.仅限于类型 int 和 float.

7. E++ 和 ++E , "++" 这个运算子的目的是把表达式的值加1,运算子放在表达式之前,
   就是在使用表达式之前将它的值加1,放在后面就是在使用之后加1.
   例如:

   " a = 3; b = ++a; " 它的结果就是 " a = 4, b = 4 "
   " a = 3; b = a++; " 它的结果就是 " a = 4, b = 3 "

   只有 int 类型能进行这种操作.

8. E-- 和 --E , "--" 这个运算子的目的是把表达式的值减去1,运算子放在表达式之前,
   就是在使用表达式之前将它的值减去1,放在后面就是在使用之后减去1. 道理同上.
   只有 int 类型能进行这种操作.

*****条件逻辑运算:

1. E1 && E2 ,当 E1 和 E2 都为 "真" 时,返回 "真" ,否则返回 "假" .

2. E1 || E2 ,当 E1 和 E2 中有一项为 "真" 时就返回 "真" ,否则返回 "假" .

3. !E 当 E 为 "真" 时返回 "假" ,当 E 为 "假" 时返回 "真" .

*****比较操作运算:

1. E1 == E2 ,当 E1 和 E2 相等时返回 "真" ,可以用于 int float string array 
   和 mapping 类型的比较.

2. E1 != E2 ,当 E1 和 E2 不相等时返回 "真" ,可以用于 int float string array 
   和 mapping 类型的比较.

3. E1 > E2 ,当 E1 大于 E2 时返回 "真" ,可以用于除了array,mapping以外的其他类型.

4. E1 < E2 ,当 E1 小于 E2 时返回 "真" ,可以用于除了array,mapping以外的其他类型.

5. E1 >= E2 ,当 E1 大于或者等于 E2 时返回 "真" ,可以用于除了array,mapping以外的其他类型.

6. E1 <= E2 ,当 E1 小于或者等于 E2 时返回 "真" ,可以用于除了array,mapping以外的其他类型.
