Higher Order PHP - start from callback

 Wed, 22 Jul 2009 00:00:06 +0800

PHP 5.3.0開始,有一個重要的特性正式加到PHP裡面,就是匿名函數。(請參考:Anonymous functions。手冊還有提到,匿名函數目前是用 Closure內部類別實作,但是不要管他,因為實作方式有可能修改。)

在提到匿名函數之前,先看看PHP的callback應用。callback是php非常好用的功能,可以利用他來用自訂的方式處理資料,例如array_map,就可以用一個callback函數一次處理陣列中所有元素,不用iteration,也不用loop。下面是用callback函數處理一個整數陣列,傳回每個元素的平方:

function power($v) {
    return $v*$v;
}
print_r(array_map("power", range(1,10)));

結果是:

Array
(
    [0] => 1
    [1] => 4
    [2] => 9
    [3] => 16
    [4] => 25
    [5] => 36
    [6] => 49
    [7] => 64
    [8] => 81
    [9] => 100
)

在php 5.3定義匿名函數很簡單,底下是一個例子:

$func = function ($name) {
    echo "\"anonymous function invoked with params: ".$name;
};
$func("hello anonymous function\n\"");

像這樣可以把一個匿名函數指派給$func變數,然後透過$func變數就可以使用。執行結果如下:

"anonymous function invoked with params: hello anonymous function"

一般的場合大概比較少這樣用就是了(看習慣)。PHP 5.3.0之前的版本,callback函數要用字串來指派,到了PHP 5.3.0則可以直接用匿名函數來指派。透過匿名函數定義中新增的use語法來運用外層scope的變數,就可以完整使用到Closure的便利性。以array_map為例,之前的callback只能針對個別元素做處理,如果你想做做出所有元素累加的功能,就會被迫使用global變數,這樣既不優雅,又缺乏重用性。利用Closure就可以用簡潔的語法做出這樣的功能:

function sumUp() {
    $init = 0;
    return function ($v) use(&$init) {
        $init += $v;
        return $init;
    };
}
print_r (array_map(sumUp(), range(1,5)));
print_r(array_map(sumUp(), range(1,10)));

這樣執行的結果是:

Array
(
    [0] => 1
    [1] => 3
    [2] => 6
    [3] => 10
    [4] => 15
)
Array
(
    [0] => 1
    [1] => 3
    [2] => 6
    [3] => 10
    [4] => 15
    [5] => 21
    [6] => 28
    [7] => 36
    [8] => 45
    [9] => 55
)

雖然用起來還是有點囉唆(要用use來宣告要用到的外層變數),不過PHP的表現力又往前進了一大步。在上面的例子也展示了Higher Order Function的特性:函數可以當作變數傳遞,可以當作傳給函數的參數,也可以當作函數的返回值。