PHP 5.3の新機能と変更点

第2回 クロージャとgoto文

この記事を読むのに必要な時間:およそ 4 分

クロージャ

クロージャとは関数の一種で,多くのスクリプト系言語がサポートしている機能です。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などのように別の名前を付ければよいのですが,次々に新しい名前を付けなければならないかも知れません。

著者プロフィール

大垣靖男(おおがきやすお)

University of Denver卒。同校にてコンピュータサイエンスとビジネスを学ぶ。株式会社シーエーシーを経て,エレクトロニック・サービス・イニシアチブ有限会社を設立。
オープンソース製品は比較的古くから利用し,Linuxは0.9xのころから利用している。オープンソースシステム開発への参加はエレクトロニック・サービス・イニシアチブ設立後から。PHPプロジェクトでは,PostgreSQLモジュールのメンテナンスを担当している。

URLhttp://blog.ohgaki.net/

著書

コメント

  • 無名関数 != クロージャ

    このページの内容だけ読むと、無名関数とクロージャが同じものだと誤解するかもしれません。必ず次のページも読んでください。
    というか、次のページからがクロージャの説明です。基本的にクロージャと無名関数はまったく関係の無い概念です。このページの内容に嘘があるとまでは言いませんが、「クロージャはラムダ関数,無名関数と呼ばれることもあります。」は疑問です。(たしかに呼ばれることもありますが。。。)

    Commented : #1  Senju (2010/05/25, 09:01)

コメントの記入