PHP - 無名関数・匿名関数

公開日:2020-12-22 更新日:2020-12-22
[PHP]

1. 概要

関数は、動的に定義して、変数に代入して使うことができます。
この時の関数は、名前を付ける必要がないため、無名関数または匿名関数と呼ばれています。
関数を使う時は、変数の後ろに ( ) を付けて、関数のようにして使います。
生成された関数は、変数の中にだけ存在するため、変数が破棄されたり、参照できなくなると、使用できなくなります。

//関数を変数に代入
$add = function($v1, $v2) {
  return $v1 + $v2;
};

//変数の中の関数の実行
var_dump( $add(1, 2) );
実行結果
int(3)

2. 使用例1

関数の処理の一部分を、関数の利用者が用意した処理にすることかできます。

次のプログラムは、無名関数を使わずに、配列の最大値、最小値、合計を求める関数です。
function getMax($values) {
    $result = $values[0];
    for ($i = 1; $i < count($values); $i++) {
        if ($values[$i] > $result) $result = $values[$i];
    }
    return $result;
}

function getMin($values) {
    $result = $values[0];
    for ($i = 1; $i < count($values); $i++) {
        if ($values[$i] < $result) $result = $values[$i];
    }
    return $result;
}

function getSum($values) {
    $result = $values[0];
    for ($i = 1; $i < count($values); $i++) {
        $result = $result + $values[$i];
    }
    return $result;
}

$values = [1, 2, 3, 4, 5];
var_dump( getMax($values) );
var_dump( getMin($values) );
var_dump( getSum($values) );
実行結果
int(5)
int(1)
int(15)

プログラムを見るとわかりますが、各関数の違いは、for の中の1行だけです。
これを、無名関数を使うと、次のようにすることができます。
//メインの関数
function getValue($f, $values) {
    $result = $values[0];
    for ($i = 1; $i < count($values); $i++) {
        //引数で渡された無名関数の実行
        $result = $f($result, $values[$i]);
    }
    return $result;
}

//無名関数の定義
$funcMax = function($result, $value) {
    return $value > $result ? $value : $result;
};
$funcMin = function($result, $value) {
    return $value < $result ? $value : $result;
};
$funcSum = function($result, $value) {
    return $result + $value;
};

$values = [1, 2, 3, 4, 5];
var_dump( getValue($funcMax, $values) );
var_dump( getValue($funcMin, $values) );
var_dump( getValue($funcSum, $values) );
実行結果
int(5)
int(1)
int(15)

誰でも使える汎用的な関数には、利用者専用の処理を入れることができません。
そこで、利用者専用の処理を無名関数として引数で渡してもらい、それを実行することで、
メインとなる関数を変更せずに、汎用性を維持したまま、利用者専用の処理を実行できるようになります。
ゲーム機に例えると、メインの汎用的な関数がハードで、無名関数がソフトにあたります。

最初からこのように実装することもありますし、
似たような関数が増えてきた時に、途中から汎用的にする場合もあります。

ちなみに、メインとなる関数から、引数で渡した関数を呼んでもらうことを、コールバックと呼びます。

3. 使用例2

次のプログラムは、メインとなる関数の進捗率の通知で、無名関数を使っています。
プログラム的には使用例1と似ていますが、無名関数がメインの関数の処理結果に影響を与えない点が異なります。
また、無名関数を変数に入れずに、そのまま関数の引数にして渡しています。

//メインの関数
function getSum($f, $values) {
    $result = $values[0];
    for ($i = 1; $i < count($values); $i++) {
        $result = $result + $values[$i];
        
        //引数で渡された無名関数の実行
        $f( ($i + 1) / count($values) );
    }
    return $result;
}

$values = [1, 2, 3, 4, 5];
$result = getSum(function($rate) {
    print("計算の進捗率:" . $rate * 100 . "%\n");
}, $values);

var_dump($result);
実行結果
計算の進捗率:40%
計算の進捗率:60%
計算の進捗率:80%
計算の進捗率:100%
int(15)