PHPの無名関数を使って再帰処理を行う

こんにちは、小川です。

今回はSymfonyではなくPHPのお話しです。PHP 5.3から無名関数が使えるようになりました。この無名関数、使い方によっては再帰なプログラムを書くことも可能なのです。

PHPの無名関数は次のようなものです。


<?php
$greet = function($name) {
  echo "Hello, {$name}.";
};
$greet('fivestar'); #=> Hello, fivestar.

無名関数というのは、その名の通り関数名が存在しない関数になります。JavaScriptなどではお馴染みですね。様々な使い方ができると思いますが、よく使うのはコールバック関数として使う方法が挙げられます。

例えばPHPにはarray_mapという標準関数があります。この関数は第1引数にコールバックを、第2引数に配列を指定し、指定した配列の各要素に対して第1引数で指定したコールバックを適応させるためのものです。次の例は、array_mapを利用して配列のそれぞれの要素から母音を取り除くというものです。


<?php
$array = array('banana', 'strawberry', 'yudoufu');
$result = array_map(function($string) {
  $vowels = array('a', 'i', 'u', 'e', 'o');
  return str_replace($vowels, '', $string);
}, $array);
echo join(', ', $result); #=> bnn, strwbrry, ydf

その場所でしか使わないような処理のためにわざわざ関数を作るのも面倒ですし、これらの一連の流れがメソッドなどの特定のスコープの中で行われている場合はそのスコープの内部で完結するため、名前空間を汚染せずに済みます。

さて本題の再帰処理です。RubyのArray#flattenと同じようなものを実装してみます。Array#flattenはネストした配列を1次元の配列に平滑化するメソッドです。無名関数にする必要はないですが、再帰のサンプルと言うことで...


<?php
// use を使って無名関数への参照を渡す
$flatten = function($array) use( &$flatten) {
  $result = array();
  foreach ($array as $value) {
    if (is_array($value)) {
      // 配列ならこの無名関数を再度呼び出す
      $result = array_merge($result, $flatten($value));
    } else {
      $result[] = $value;
    }
  }
  return $result;
};
$array = array(1, 5, array(3, array(22, 5)), 12);
$result = $flatten($array); 
echo join(', ', $result); #=> 1, 5, 3, 22, 5, 12

先ほどまでと大きく違うのがuseを使用しているところです。useとは、無名関数の外にあるスコープの変数を無名関数内に渡すための仕組みです。関数でいうglobalや、メソッドからオブジェクトのプロパティを参照するようなものです。再帰を行う場合、ここに無名関数を格納する変数への「参照」を渡します。そうでないと、定義されていない変数を渡すことになりエラーになります。

無名関数を活用することで余計な関数やメソッドを定義せずに済んだりもしますが、あまり使いすぎるとそれはそれで可読性の低下につながる可能性もあります。特にPHPで実装されたのは最近の話ですので、見慣れない方もいるでしょう。何事もほどほどに活用していきましょう。