関数内関数

PHPのマニュアルみてたら、こんなのがあった。今の今まで、ものすごい勢いで見逃してた。いまさらだけども、知らんかったなあこれ、できたんだ〜。コンパイル言語っぽい書き方だって気がしてたから、phpでできるとは思ってなかった。*1

pascal(というかDelphi言語)で、関数内関数はよく使ってた。

メソッド内で冗長にならざるを得ない箇所があり、privateで外出しするほどフレキシブルな処理でもないときに、関数内関数が多いに役に立った(あとは再帰とか)。クロージャも自分の中では、要するに関数内関数みたいなもんだろ?って始めは理解していた(というかこの理解の仕方は未だ変わっていないのだが)。

create_functionするよりもこっちのほうがおれは好きだな。懐かしい書き方だったので思わずメモる。

が、こんな気になるトピックも。

公式サイトのマニュアルに載っている、関数内関数ですが…
http://www.php.net/manual/ja/language.functions.php#AEN4977

以下のスクリプトを作成し(/home/www/hoge.phpとしましょう)、

');
}
bar();
}

foo(); // ←1回目
foo(); // ←2回目
?>

実行してみた。

$ php -v
PHP 5.2.2 (cli) (built: May 30 2007 10:42:19) 
Copyright (c) 1997-2007 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2007 Zend Technologies
$ php hoge.php
bar<br>
Fatal error: Cannot redeclare bar() (previously declared in /home/admin/hoge.php:4) in /home/admin/hoge.php on line 3

あちゃー、変わってないや。それが仕様ってことなんか。関数内関数を定義した関数は、一回しか呼べないってことになるんかい。

だが、そこはPHP。こうすれば回避可能だそうだ。

<?php
function foo(){
    if (!function_exists('bar')) {
        function bar(){
            echo('bar<br>');
        }
    }
    bar();
}

foo(); // ←1回目
foo(); // ←2回目
?>

ここらへんはまさにLLって感じだな。覚えとこう。下記は実行結果。

$ php hoge.php
bar<br>bar<br>

だけど、LLにはクロージャがよく似合う。phpもcreate_functionじゃなくて、クロージャを実装するべきだ。perlみたく、代入 or 定義すると、関数のポインタ(リファレンスか)が返ってくるようなヤツ。

昔、perlクロージャを使ったとき、スコープの関係でメモリリーク起こしたことあったけれど、今となっては懐かしき思ひ出。

(追記)
JavaScriptは完璧だった。

<html>
    <head>
        <script>
            function foo(){
                function bar(){
                    document.write('bar<br>');
                }
                bar();
            }

            foo();
            foo();
        </script>
    </head>
</html>

*1:JavaScriptでも同様な書き方ができる