クロージャ
クロージャとは関数の一種で,多くのスクリプト系言語がサポートしている機能です。Perl,Ruby,Pythonなど,モダンなスクリプト系言語すべてがサポートしています。JavaScriptもクロージャをサポートしいます。JavaScriptプログラミングではクロージャがよく利用されるので使い方をご存知の方も多いと思います。
クロージャは関数がアクセス可能な変数が,環境によって決まります。この特徴を利用してオブジェクト指向設計よりも分かりやすい設計が行えたり,呼び出された関数の内部から呼び出すコールバック関数として様々な動作を行わせることが出来ます。
クロージャの使い方
クロージャはラムダ関数,無名関数と呼ばれることもあります。無名関数の名前の通りクロージャは名前が無い関数を定義して利用します。
クロージャに慣れていない方は,名前が無い関数をどうやって使うのか?どうして便利なのか?と疑問に思うかも知れませんが,例をいくつか見れば,その便利さが分かります。
クロージャの構文は次のように,関数定義の名前定義が無いものと言っても構いません。最後に“;”(セミコロン)があることに注意してください。
- 無名関数の構文
- function( [ 引数 ] ... ) { 文 };
PHP以外の言語では,グローバルスコープの変数に関数内部からもいつでもアクセスできるようになっています。PHPの場合は$GLOBALS変数を使用するかglobal宣言してから利用しなければなりません。無名関数の中からも同じような制限があります。
無名関数の中から無名関数が実行されるスコープ内の変数を使用する場合には,“use”文で使用を宣言しなければなりません。
- use文を利用する場合の無名関数の構文
- funciton( [ 引数 ] ... ) use ( [ 変数 ] ... ) { 文 };
多少面倒に思うかも知れませんが,明示的に指定した変数以外はアクセスできないので,誤って意図しない変数を書き換えてしまったり,利用してしまうことを防ぐことができます。
無名関数は関数名が無いので,変数に格納するか,関数の引数として直接渡します。
例:無名関数を変数から利用する
<?php
$writeline = function($msg) { echo $msg.PHP_EOL; };
$writeline('ABC');
$writeline('XYZ');
?>
出力
ABC XYZ
これだけでは何が便利なのかさっぱり分からないですが,使い方だけは分かったと思います。
function($msg) { echo $msg.PHP_EOL; };
の部分が無名関数の定義です。無名関数は直接パラメータとしてほかの関数に渡すことが出来ます。
例:無名関数を利用してarray_walkで配列の中身を出力
<?php
$fruits = array("lemon"=>156, "orange"=>210, "banana"=>180, "apple"=>343);
array_walk($fruits,function($price,$name) { echo $name.':'.$price.PHP_EOL; });
?>
出力
lemon:156 orange:210 banana:180 apple:343
無名関数が無いPHP 5.2場合,パラメータとして渡している
function($price,$name) { echo $name.':'.$price.PHP_EOL;}
を別の関数として定義した上で,変数関数としてarray_walk関数に渡さなければなりません。
例:無名関数を利用しない方法
<?php
$fruits = array("lemon"=>156, "orange"=>210, "banana"=>180, "apple"=>343);
function callback($price,$name) { echo $name.':'.$price.PHP_EOL; }
array_walk($fruits,'callback');
?>
あまり,複雑でないのでわかりづらいかも知れませんが,無名関数を利用すると簡潔に記述できることが分かります。
無名関数を使うと,クラスを使ったオブジェクト指向設計を行わなくても,一つの関数に複数の動作を行わせることも簡単です。
例:処理を無名関数として渡す
<?php
$square = function($x) { return ($x * $x); };
$increment = function($x) { return ++$x; };
function foo($param, $op) {
return $op($param);
}
echo foo(3, $square) . PHP_EOL;
echo foo(4, $increment) . PHP_EOL;
?>
変数関数に書き換えると次のようになります。
例:変数関数の場合
<?php
function square($x) { return ($x * $x); }
function increment($x) { return ++$x; };
function foo($param, $op) {
return $op($param);
}
echo foo(3, 'square') . PHP_EOL;
echo foo(4, 'increment') . PHP_EOL;
?>
どちらも全く同じことができますが,大きな違いがあります。変数関数の場合,名前付き関数として定義しなければならないため,名前空間を使用していしまいます。処理する変数が数値でなく配列の場合は要素すべてを2乗したりインクリメントしたりする,という関数を新たに追加する場合に‘square’という関数名は既に使われているので使えません。
‘square_array’などのように別の名前を付ければよいのですが,次々に新しい名前を付けなければならないかも知れません。

