一篇偶翻译的文档,请指正。
LPC帮助文档—变量类型—函数
基本概念:
MudOS 有一个叫做“function”的变量类型,这种变量可以
用于指向一种普遍的函数。你可能已经熟悉传递一个函数到
某个 efun 的主意。拿取,比如说 filter_array这个efun,
它获取一个数祖,然后返回一个其每个元素都在某个函数中
返回非零的数组。习惯上,这个过程是由传递一个物件和一
个函数的名称来完成的。现在呢,它还可以由传递一个函数
型的表达式来完成,这个表达式只是包括了了一个可以稍后
进行计算的函数。
函数指针可以被创建和分派到一个变量,例如:
function f = (: local_func :);
以及被传递到其他的常规情况或者 efuns,就像普通的值一
样:
foo(f); map_array( ({ 1, 2 }), f);
或者在稍后进行计算:
x = evaluate(f, "hi");
当最后一行被运行后,f 指出的这个函数就被呼叫了,而“
hi”则被作为一个实参。这样做将达到与下面同样的效果:
x = local_func("hi");
使用一个函数指针的优势是:如果此后你想运行的是另一个
函数,你可以仅仅改变变量的值就行了。
值得注意的是,如果 evaluate() 被输入一个不是函数的值,
它将原封不动的把这个值再传回来。你可以这样试试:
void set_short(mixed x) { short = x; }
mixed query_short() { return evaluate(short); }
这么做的话,一般的物件可以仅仅是:set_short("任意"),
而特殊些的就可以通过:set_short((: short_func :));
来多样化的改变自己的短描述。
有用的几种函数指针:
上面这个就是一个最简单的函数指针,它们仅仅是指向同一
个物件中的一个本地函数。调用时,参数也可以包括在内,
例如:
string foo(string a, string b)
{
return "(" + a "," + b + ")";
}
void create()
{
function f = (: foo, "left" :);
printf("%s %s\n", evaluate(f), evaluate(f, "right"));
}
将会打印出:(left,0) (left,right)
第二种情况则是仅仅是像 (: efun_name :) 这样的 efun指
针。这种这本地函数指针是很相似的,例如 objects efun
可以获取一个可选的函数,返回所有通过此函数的结果是真
值的物件,因此:objects((: clonep :)) 将会返回一个包
括游戏中所有是复制品的物件。当然,也可以带上参数:
void create()
{
int i;
function f = (: write, "Hello, world!\n" :);
for (i = 0; i < 3; i++) { evaluate(f); }
}
将会打印出:
Hello, world!
Hello, world!
Hello, world!
注意 simul_efun 在和函数指针的关系上是与 efun一样的。
第三种是和 MudOS 通常支持的类似的 call_other函数指针,
格式是:(: object, function :)。如果要使用参数,参数
要被假如一个在函数名前面的数组中。下面是例子:
void create()
{
string *ret;
function f = (: this_player(), "query" :);
ret = map(({ "name", "short", "long" }), f);
write(implode(ret, "\n"));
}
这样将会打印出 this_player()->query("name")、
this_player()->query("short") 和
this_player()->query("long") 的结果。
要使一个函数指针直接呼叫 query("short"),可以使用:
f = (: this_player(), ({ "query", "short" }) :)
下面是其他的一些同样效果的方法提供参考:
// 使用 call_other efun 的 efun 指针
f = (: call_other, this_player(), "query", "short" :)
// 一个表达式的功能,看看下边
f = (: this_player()->query("short") :)
第四种则是表达式函数指针。这是以 (: expression :) 的
形式来使用的。在一个表达式函数指针中,参数可以写成类
似于 $1、$2、$3...这样的形式,例如:
evaluate((: $1 + $2 :), 3, 4) // 返回 7.
在用到 sort_array 时,它将很有用,例如:
top_ten = sort_array(player_list,
(: $2->query_level() - $1->query_level :) )[0..9];
第五种可以称为无名的函数指针:
void create()
{
function f = function(int x)
{
int y;
switch(x)
{
case 1: y = 3;
case 2: y = 5;
}
return y - 2;
};
printf("%i %i %i\n", (*f)(1), (*f)(2), (*f)(3));
}
将会打印出:1 3 -2
注意那个 (*f)(...) 是和 evaluate(f, ...)一样而且被保
留作向后兼容。凡是在一般函数中合法的东西在无名函数中
就是合法的。
函数将在什么时候被运行呢?
有一条原则是:在 efun、本地函数和 simul_efun函数指针
被创建时,作为其参数的函数就被运行了。如果是一个表达
式形式的函数指针,则是要等到此函数指针确实地被使用时
才运行:
// 当此函数指针创建时它就将 destruct 所有是
// this_player() 的人
(: destruct, this_player() :)
// 当此函数运行时才 destruct 所有是 this_player()
// 的人
(: destruct(this_player()) :)
因此,在一个表达式指针中,使用一个本地变量是违法的。
因为等到这个函数指针运行的时候,可能这个变量已经不存
在了。然而,还是有一种解决的方法:
// 和上面的第一个例子一样
(: destruct($(this_player)) :)
$(whatever) 的意思就是立即计算whatever,保持着这个值,
直到这个函数被运行的时候再拿出来用。这还可以使事情做
得更有效率:
map_array(listeners,
(: tell_object($1, $(this_player()->query_name()) + " bows.\n") :));
上面的这个 $(this_player()->query_name()) 只需要被计
算一次,而不是每次消息时都再计算一次。当然增加的那段
也可以预先做好:
map_array(listeners,
(: tell_object($1, $(this_player()->query_name() + " bows.\n")) :));
注意,在这种情况下我们也可以这样做(效率最低的方式):
map_array(listeners,
(: tell_object, this_player()->query_name() + " bows.\n" :));
翻译使用的词汇:
evaluate : 运行、计算
function pointer : 函数指针
argument : 参数
local : 本地
variable : 变量
whatever : 任意
--------------------------------------------------------------------------------
莫愁前路无知己,天下谁人不识君?
原文
作者:jjgod 发表时间:2001年8月4日 10:42
--------------------------------------------------------------------------------
General Concept:
----------------
MudOS has a variable type named 'function'. Variables of this type may
be used to point to a wide variety of functions. You are probably already
familiar with the idea of passing a function to certain efuns. Take, for
example, the filter efun. It takes an array, and returns an array containing
the elements for which a certain function returns non-zero. Traditionally,
this was done by passing an object and a function name. Now, it can also
be done by passing an expression of type 'function' which merely contains
information about a function, which can be evaluated later.
Function pointers can be created and assigned to variables:
function f = (: local_func :);
Passed to other routines or efuns, just like normal values:
foo(f); map_array( ({ 1, 2 }), f);
Or evaluated at a later time:
x = evaluate(f, "hi");
When the last line is run, the function that f points to is called, and "hi"
is passed to it. This will create the same effect as if you had done:
x = local_func("hi");
The advantage of using a function pointer is that if you later want to
use a different function, you can just change the value of the variable.
Note that if evaluate() is passed a value that is not a function, it just
returns the value. So you can do something like:
void set_short(mixed x) { short = x; }
mixed query_short() { return evaluate(short); }
This way, simple objects can simply do: set_short("Whatever"), while objects
that want their shorts to change can do: set_short( (: short_func :) );
Available kinds of function pointers:
-------------------------------------
The simplest function pointers are the ones shown above. These simply
point to a local function in the same object, and are made using
(: function_name :). Arguments can also be included; for example:
string foo(string a, string b) {
return "(" + a "," + b + ")";
}
void create() {
function f = (: foo, "left" :);
printf( "%s %s\n", evaluate(f), evaluate(f, "right") );
}
Will print: (left,0) (left,right)
The second kind is the efun pointer, which is just (: efun_name :). This
is very similar to the local function pointer. For example, the objects()
efun takes a optional function, and returns all objects for which the
function is true, so:
objects( (: clonep :) )
will return an array of all the objects in the game which are clones.
Arguments can also be used:
void create() {
int i;
function f = (: write, "Hello, world!\n" :);
for (i=0; i<3; i++) { evaluate(f); }
}
Will print:
Hello, world!
Hello, world!
Hello, world!
Note that simul_efuns work exactly like efuns with respect to function
pointers.
The third type is the call_other function pointer, which is similar to the
type of function pointer MudOS used to support. The form is
(: object, function :). If arguments are to be used, the should be added
to an array along with the function name. Here are some examples:
void create()
{
string *ret;
function f = (: this_player(), "query" :);
ret = map( ({ "name", "short", "long" }), f );
write(implode(ret, "\n"));
}
This would print the results of this_player()->query("name"),
this_player()->query("short"), and this_player()->query("long").
To make a function pointer that calls query("short") directly, use:
f = (: this_player(), ({ "query", "short" }) :)
For reference, here are some other ways of doing the same thing:
f = (: call_other, this_player(), "query", "short" :) // a efun pointer using
// the call_other efun
f = (: this_player()->query("short") :) // an expression functional; see
// below.
The fourth type is the expression function pointer. It is made using
(: expression :). Within an expression function pointer, the arguments
to it can be refered to as $1, $2, $3 ..., for example:
evaluate( (: $1 + $2 :), 3, 4) // returns 7.
This can be very useful for using sort_array, for example:
top_ten = sort_array( player_list,
(: $2->query_level() - $1->query_level :) )[0..9];
The fifth type is an anonymous function:
void create() {
function f = function(int x) {
int y;
switch(x) {
case 1: y = 3;
case 2: y = 5;
}
return y - 2;
};
printf("%i %i %i\n", (*f)(1), (*f)(2), (*f)(3));
}
would print: 1 3 -2
Note that (*f)(...) is the same as evaluate(f, ...) and is retained for
backwards compatibility. Anything that is legal in a normal function is
legal in an anonymous function.
When are things evaluated?
--------------------------
The rule is that arguments included in the creation of efun, local function,
and simul_efun function pointers are evaluated when the function pointer is
made. For expression and functional function pointers, nothing is evaluated
until the function pointer is actually used:
(: destruct, this_player() :) // When it is *evaluated*, it will destruct
// whoever "this_player()" was when it
// was *made*
(: destruct(this_player()) :) // destructs whoever is "this_player()"
// when the function is *evaluated*
For this reason, it is illegal to use a local variable in an expression
pointer, since the local variable may no longer exist when the function
pointer is evaluated. However, there is a way around it:
(: destruct( $(this_player) ) :) // Same as the first example above
$(whatever) means 'evaluate whatever, and hold it's value, inserting it
when the function is evaluated'. It also can be used to make things more
efficient:
map_array(listeners,
(: tell_object($1, $(this_player()->query_name()) + " bows.\n") :) );
only does one call_other, instead of one for every message. The string
addition could also be done before hand:
map_array(listeners,
(: tell_object($1, $(this_player()->query_name() + " bows.\n")) :) );
Notice, in this case we could also do:
map_array(listeners,
(: tell_object, this_player()->query_name() + " bows.\n" :) );
尊重作者 转载请注明出处52mud.com