function
一般概念 :
MudOS 有一種稱為 function (函式指標) 的變數型態. 這種型態的變數可以用來指向各種不同的函式.
您也許早已熟悉把一個函式當作某些外部函式 (efuns) 參數. 舉例來說, 像是過濾陣列的外部函式.
它讀入一個陣列, 並經由一個函式過濾陣列中的元素. 讓此函式傳回非零值 (non-zero)
的元素就保留下來, 結果傳回一個新的陣列. 傳統上, 這樣做要傳入一個物件和函式的名稱.
現在, 可以用函式指標這種變數型態做到. 函數指標只含有一個函式的資訊, 可以在稍後執行其函式.
函式指標可以創造並指定為變數:
function f = (: local_func :);
上面的 f 可以用於其他程式流程或外部函式中, 如同普通的變數值:
foo(f); map_array( ({ 1, 2 }), f);
或是稍後再執行 f:
上一行執行時, 會呼叫 f 指向的函式, 而 "hi" 當成參數傳入函式. 其效果同下:
使用函式指標的好處是, 如果您想使用不同的函式, 只需要改變函式指標變數的值.
注意 evaluate() 的參數如果不是函式, 就只會傳回參數值. 所以您可以做以下的
事:
void set_short(mixed x) { short = x; }
mixed query_short() { return evaluate(short);
}
這樣, 簡單的物件可以只用 set_short("Whatever"); 以達成
set_short( (: short_func :) ); 的效果.
目前函式指標的種類 :
最簡單的函式指標如上面所述. 只是簡單地指向同一個物件中的一個區域函式
(local function), 即 (: function_name :). 函式指標的值可以包括函式的參數,
例如:
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 :). 這樣與區域函式指標很類似.
譬如說, objects() 外部函式要傳入函式, 並傳回任何使函式為真值的物件. 所以:
會傳回遊戲中所有的複製物件 (clones). 您也可以使用參數:
就會印出:
Hello, world!
Hello, world!
Hello, world!
注意, 對函式指標來說, simul_efuns (模擬外部函式) 與外部函式是相同的.
第三種形式是 call_other 函式指標, 與以前 MudOS 所使用的函式指標型態類似.
其形式為 (: 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" })
:)
以下是達到同樣目的的做法參考:
f = (: call_other, this_player(), "query", "short"
:) /* 一個外部函式指標, 使用 call_other */
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];
第五種形式是不知名 (anonymous) 函式:
會印出: 1 3 -2
注意, (*f)(...) 等於 evaluate(f, ...) , 保留這種語法是為了與舊版相容.
任何普通函式合法 (legal) 的寫法, 都可以用於不知名函式.
什麼時候執行 (evaluate) 函式 ?
創造帶有參數的外部函式、區域函式、模擬外部函式指標時, 會執行函式. 而運算式和功能性
(functional) 函式指標, 只有在使用函式指標時執行函式:
(: destruct, this_player() :) /*
創造這個函式指標時, 就會摧毀 this_player(). 函式於創造時執行. */
(: destruct(this_player()) :) /*
在稍後使用此函式指標時, 才會摧毀 this_player()" */
在此, 不可以在運算式函式指標裡, 使用一個區域變數. 因為執行這個函式指標之後,
這個區域變數就不存在了. 但是可以用下面這個方法解決:
(: destruct( $(this_player) ) :)
// 與上面第一個範例相同.
$(whatever) 表示要執行 whatever, 並保留其值. 當執行此函式時, 再傳入這個值.
這樣一來可以更有效率:
map_array(listeners,
(: tell_object($1, $(this_player()->query_name())
+ " bows.\n") :) );
只做一次 call_other , 而不需要每個訊息都做. 也可以事先合併字串:
map_array(listeners,
(: tell_object($1, $(this_player()->query_name()
+ " bows.\n")) :) );
注意, 在這個情形下, 也可以這樣做:
map_array(listeners,
(: tell_object, this_player()->query_name() +
" bows.\n" :) );
翻譯: Spock @ FF 97.Aug.10.
回到上一頁