海贼王之鸿蒙兑换系统:mud中的函数调用

来源:百度文库 编辑:中科新闻网 时间:2024/04/19 16:00:39

应该有说明吧

Lpc的函数

第一节 序言

在前面的介绍中,大家应该知道了Lpc的Object包含能处理变量的函数。
当函数被执行时,它的工作就是处理操作变量,还有是调用(call)别的函
数。变量在函数中被改变操作。变量必须有个数据类型使得计算机能明白
它指向的内存中"0"和"1"到底是什么东西。一个Object的性质通常由它的
包含的变量确定,但是它的特性的表现却是依赖于它包含的函数。一个
Object如果不含有任何一个函数那是不可想象的。那么:什么是函数。

第二节 函数

2.1 什么是函数?
和数学的函数一样,你给Lpc的函数一个值,它能返回一个值。有些语
言,比如Pascal,会区分过程和函数。Lpc和C/C++一样,没有过程,但是
明白这种区别还是有用的。Pascal叫做过程的东西,Lpc叫做类型是void
的函数。换句话说,过程就是什么都不返回的函数。Pascal叫做函数的,
必须返回一些东西。在Lpc中,最无聊的,最简单的,但也是正确的函数
是这样的:

-----
void eventDoNothing() {}
-----

这个函数不接收任何输入,不执行指令,也不返回任何值。

每一个Lpc函数都由三部分组成:
1) 函数声明
2) 函数定义
3) 函数调用

和变量一样,函数必须先有个声明。这样可以让Driver知道:
1) 这个函数将返回的是哪种数据类型。
2) 需要的输入是什么,多少。通常把输入叫做参数。
一个函数声明通常是这样的:
类型 函数名(参数1, 参数2, ..., 参数N);
下面是一个函数声明的例子,这个函数叫 DrinkWater,有一个string
类型的参数,返回的是一个int。

-----
int eventDrinkWater(string str);
-----

在上面的声明中, str是输入的参数的变量名,也可以没有。就是说可以
象下面这样声明 eventDrinkWater()

-----
int eventDrinkWater(string);
-----

函数定义就是代码,它描述了这个函数对传人的参数究竟做了些什么。
函数调用就是别的函数在任何地方使用执行了这个函数。一个函数在它
写完后永远不会被调用,那这个函数的存在的唯一意义只能是浪费内存和
硬盘。一个函数写出来的目的是为了被调用。
下面是两个函数相互调用的例子,两个函数是 eventPrintValue() 和
add(),

-----
/* 首先是函数声明,这个通常是在一个Object的开始部分。
*/

void eventPrintValue();
int add(int x, int y);

/* 其次是函数 write_vals() 的函数定义。我们假定这个函数将被调用
* 是为了描述这个Object.
*/
void eventPrintValue()
{
int x;

x = add(2, 2); // 我们指定 x 接收调用函数 add() 后返回的值。
write(x + "\n");
}

/* 最后是函数 add() 的函数定义。 */
int add(int x, int y)
{
return (x + y);
}
-----

有一点是指明的,在XO的编程的风格我们要求所有的函数都必须有声
明,这个在我们最开始时候说明过。但是实际上必须有函数声明的函数
是那些被调用在函数定义之前的函数。我们规定必须有函数声明,这个
只是规定,但是它会给编程带来好处。

在这一节我们知道什么是函数,函数是由什么组成。要记住,写一个
函数的根本目的是为用它,调用它。一个函数永远不会被调用,那它就
失去了存在的价值。通常别人使用你写的函数,通常只关心它能对传人
的参数做些什么加工,就是这个函数的功能是什么,返回什么。因此一
个函数有一个好的函数名,能直接描述这个函数的功能是很重要的。我
在第一章中说明了XO规定的对函数的命名机制。采用统一的命名方式有
助于相互合作提高效率。

2.2 Efuns
也许你已经听说过efun这个词了,他们是外部定义的函数,是
externally defined function 的缩写。就是说,他们是由Mud Driver
定义好的。如果参加过Lpc的编程,或者看过Lpc的代码,你可能找到这
样的一些表达式:this_player(), strcmp(), implode(), filter(),
等等,看起来象是一个函数,而你找遍整个Object以及这个Object继承
的所有Object中都没有这些函数,这就表明他们是efun。efun存在价值
是因为他们执行起来要比一般的Object带有的函数速度快的多,为什么
快呢,因为他们是以计算机直接能理解的二进制的形式存在。对于Object
内部定义的函数,我们通常叫他们是lfun(local function)。一个巫师
主要工作也就是编写一些lfun组成的Object。
在上面的例子中的 eventPrintValue() 中调用了两个函数,第一个是
函数 add(), 这个是有你声明和定义的,这个就是lfun。第二次调用,
是调用函数 write() 这个函数通常就是efun。Driver已经替你声明和定
义好了。你所要做只是调用它。
efun被创立是为了
1) 处理一些很常用的,每天都有许多函数会调用的。
2) 处理internet socket的输入输出。
3) 以及一些Lpc很难处理的事,毕竟Lpc是C的很小的子集。
efun是用C写好的,内嵌在Driver里面的。在Mud起来之前,和Driver
一起编译好的,他们执行起来会快的多。但是正和你期望的一样,他们
的调用和你写的函数的调用方法是完全一样的。总的来说,需要关心的
和一般函数一样,它需要传入什么参数,它将会返回什么的东西。

怎样得到一些efun的信息,比如传入参数和返回的类型,通常在一个
Mud里面,你可以在类似这样的 /doc/efun 的目录底下找到,或者直接
用 help efun 指令就可以得到帮助。efun及其依赖于你所在的Mud
的Driver,不同的Driver带有的efun区别是很大。
对于XO,使用的是MudOS,一般的efun,只要用 help 指令就能得到
帮助,或者你多看看源码,看看别人是怎样使用的,当然你如果无论如
何也不能明白一个efun,你可以问问大巫师,他们通常会很乐意和你探
讨的。但是有一点是指出,能自己解决的问题最好自己解决。

2.3 自己动手写函数
用Lpc写Object的函数,是为了表现这个Object的特性。这个特性的
函数实际上就是一些代码按顺序排列,排列的顺序决定了这个函数。一
个函数被调用,函数的代码就按照函数定义中代码按顺序执行。在
eventPrintValue()中,下面这个语句:

-----
x = add(2, 2);
-----

必须在 efun: write() 之前调用,如果你想看到正确的结果。

为了返回这个函数执行后的值,必须这么做:使用 "return" 指令和后
面跟着和这个函数数据类型一样的一个值。在上面的 add(),这个指令是
"return ( x + y );",( x + y )的值就是返回给 eventPrintValue()
以及赋给 x 的那个值。更进一步的说,"return" 结束当前函数的执行
并把控制权返回给调用它的那个函数。实际上,return 可以返回跟在
它后面的一个表达式的值。如果想停止一个类型为void的函数的执行,
只要简单使用 "return;",不返回任何值。强调一点,想要返回的表达
式的值的数据类型 必须 和函数自己的数据类型一致。
那到底如何写一个函数(lfun)?
一个函数定义通常按次序有下面部分:
1) 函数返回数据类型
2) 函数名
3) 以 ( 开始,以 ) 结束的函数列表
4) 一个 { 标志这个函数从这里开始执行
5) 变量声明,在这个函数将要使用的变量
6) 计算机指令,各种语句,表达式,以及调用别的函数。
7) 一个 } 标志函数代码部分到次结束。除非这个函数的类型说明成
void,在 } 之前必须有个 return 返回和这个函数的数据类型,在
return 后继续执行调用这个函数的那个函数。

举个平凡的例子:

-----
int my_function( string target )
{
string name;
object ppl

name = "Trill";
if ( ppl = find_player( target ) )
{
tell_object( ppl, "hi. My name is " + name + ". \n" )
return 1;
}
else
{
tell_object( this_player(), "sorry, i don't find the target : "
+ name + ".\n" );
return 0;
}
return 0;
}
-----

这个函数接收一个 string 类型的参数,执行一些指令,返回一个 int
型的整数。

第三节 小结
定义了Lpc的Object的文件实际是由一些函数组成的。所有的函数,按
顺序由以下部分组成:
1) 函数声明
2) 函数定义(函数体)
3) 函数调用
3) The call
函数声明通常出现在这个文件的开头,在任何一个函数定义之前。必
须做到是,每个函数的被调用之前有函数的声明。在 XO 中规定严格一
些所有的函数必须有声明。
函数定义可以出现在文件的任何地方,只要在它的声明之后就可以了。
有一点要注意,不要在一个函数内部定义另一个函数。在 XO 中规定是
函数的定义按照一个给定的次序。
函数调用通常在别的函数定义的内部,如果你想要你的代码执行你写
的函数的话。当然也可以出现这个函数自己的函数定义内部,但是对于
一个新的巫师来说,最好别这么做,因为这样可能导致无限的循环。

每个 Driver 有自己一套 efun ,这些函数 Driver 已经替你声明过了,
也已经定义好了。efun 通常执行起来要比你自己写的函数快一些。更进
一步说,每个 MudLib 有一些特殊的函数看起来非常象 efun,他们也已
经声明过了,定义好了。他们被叫做 simul_efun,或者 sefun,或者叫
做 模拟 efun。对于 efun 的帮助,通常在 /doc/efun,或者
/help/wizard/efun,等类型的目录底下。很多 Mud 会提供一个指令,比
如 "man" 或 "help" 来提供在线帮助。

注意:
有些 Driver 对函数的类型检查不严格,甚至忽略你对函数那些特殊
的声明,比如 private 等等。但是不要管这个,你应该这样一个良好的
习惯,对函数的类型有严格声明。为什么?

1)对别的人(包括以后你自己)能更容易读你的程序,更快理解你这
部分代码想描述什么。这个对调试程序也是很有用的,大部分的
程序错误(包括 () 和 {} 的丢失)通常是数据类型不匹配。
2)因为这个被认为是个好的编程习惯。