質問
JavaScriptでは、ネストされた関数は非常に便利です。クロージャ、プライベートメソッド、そして何を持っていますか。
ネストされたPHP関数の目的は何ですか?誰もがそれらを使用しますか?
これは私がやった小さな調査です
<?php
function outer( $msg ) {
function inner( $msg ) {
echo 'inner: '.$msg.' ';
}
echo 'outer: '.$msg.' ';
inner( $msg );
}
inner( 'test1' ); // Fatal error: Call to undefined function inner()
outer( 'test2' ); // outer: test2 inner: test2
inner( 'test3' ); // inner: test3
outer( 'test4' ); // Fatal error: Cannot redeclare inner()
解決
基本的にはありません。これは常にパーサーの副作用として扱ってきました。
Eran Galperinは、これらの関数が何らかの形でプライベートであると誤解されており、 outer()
が実行されるまで宣言されません。また、プライベートスコープではなく、遅延はしますがグローバルスコープを汚染します。そして、コールバックとして、外側のコールバックはまだ一度しか呼び出せません。エイリアスを複数回呼び出す可能性が非常に高い配列にそれを適用することがどのように役立つかはまだわかりません。
掘り下げることができる唯一の「現実世界」の例は、 this これは1回しか実行できず、クリーナーIMOに書き換えることができます。
私が考えることができる唯一の用途は、モジュールが[name] _includeメソッドを呼び出して、グローバルスペースでいくつかのネストされたメソッドを設定することです
if (!function_exists ('somefunc')) {
function somefunc() { }
}
チェック。
PHPのOOPは明らかに良い選択です:)
他のヒント
PHP 5.3を使用している場合、匿名関数を使用すると、Javascriptに似た動作を得ることができます。
<?php
function outer() {
$inner=function() {
echo "test\n";
};
$inner();
}
outer();
outer();
inner(); //PHP Fatal error: Call to undefined function inner()
$inner(); //PHP Fatal error: Function name must be a string
?>
出力:
test
test
[@ PierredeLESPINAYのコメントに従って書き直されました。]
これは単なる副作用ではありませんが、実際にはプログラムのロジックを動的に変更するための非常に便利な機能です。これは手続き型のPHPの時代のものですが、可能な限り最も簡単な方法で特定のスタンドアロン関数の代替実装を提供したい場合は、OOアーキテクチャでも役立ちます。 (ほとんどの場合、OOがより良い選択ですが、それはオプションであり、委任ではなく、いくつかの単純なタスクは余分な作業を必要としません。)
たとえば、フレームワークからプラグインを動的/条件付きでロードし、プラグイン作成者の生活を非常に簡単にしたい場合、プラグインがオーバーライドしなかった重要な機能のデフォルトの実装を提供できます:
<?php // Some framework module
function provide_defaults()
{
// Make sure a critical function exists:
if (!function_exists("tedious_plugin_callback"))
{
function tedious_plugin_callback()
{
// Complex code no plugin author ever bothers to customize... ;)
}
}
}
関数内で定義された関数あまり使用できませんが、条件付きで定義された関数は使用できます。例:
if ($language == 'en') {
function cmp($a, $b) { /* sort by English word order */ }
} else if ($language == 'de') {
function cmp($a, $b) { /* sort by German word order; yes it's different */ }
} // etc
そして、コードで行う必要があるのは、usort()呼び出しなどで 'cmp'関数を使用することだけです。これにより、コード全体に言語チェックを散らかすことはありません。今はこれを行っていませんが、それを行うための引数を見ることができます。
上記のことはすべて、単純に入れ子関数を作成して、関数内のローカライズされた反復コード(親関数内でのみ使用される)を置き換えることができます。無名関数はこれの完璧な例です。
クラスにプライベートメソッド(またはより小さなコードブロック)を作成するだけかもしれませんが、それは超特定のタスク(親に排他的)をモジュール化する必要があるが、必ずしも利用できない場合に水を濁しますクラスの残り。良いニュースは、その関数が他のどこかに必要であることが判明した場合、修正はかなり基本的なことです(定義をより中央の場所に移動します)。
一般的に言って、他のCベースのプログラミング言語を評価するための標準としてJavaScriptを使用することは悪い考えです。 JavaScriptは、PHP、Python、Perl、C、C ++、およびJavaと比較すると、間違いなく独自の動物です。もちろん、多くの一般的な類似点はありますが、非常に細かく詳細な情報( JavaScript:The Definitive Guide、6th Edition、Chapter 1-12 を参照)に注目すると、コアJavaScriptがユニークになり、美しく、異なって、シンプルで、複雑なすべてを同時に。それが私の2セントです。
明確にするために、ネストされた関数がプライベートであると言っているわけではありません。そのネストは、些細なものをモジュール化する必要がある場合(そして親関数でのみ必要な場合)に混乱を避けるのに役立ちます。
私のphpはすべてオブジェクト指向ですが、特に関数が再帰的であり、必ずしもオブジェクトではない場合、ネストされた関数の使用が見られます。つまり、ネストされている関数の外部では呼び出されませんが、再帰的であり、その後関数である必要があります。
他の1つのメソッドを明示的に使用するための新しいメソッドを作成しても、ほとんど意味がありません。私にとってそれは不器用なコードであり、オブジェクト指向ではありません。他の場所でその関数を呼び出さない場合は、入れ子にします。
webserviceの呼び出しでは、ネストされた方法で、数千の関数で満たされたライブラリ上の個々の関数を動的に含む、はるかに低いオーバーヘッド(メモリと速度)が見つかりました。典型的なコールスタックは、5〜10コールの深さで、1ダースの1〜2 kbファイルを動的にリンクするだけで、メガバイトを含めるよりも優れている場合があります。これは、ラッピングが必要な小さなutil関数を作成するだけで行われました。含まれる関数は、呼び出しスタックの上の関数内にネストされます。すべてのWebサービスの呼び出しに必要ではないが、PHPの組み込みの遅延読み込み機能を使用することもできた数百の関数でいっぱいのクラスとは対照的に考えてください。
これは古い投稿であることは知っていますが、私はネストされた関数を使用して、ローカルでのみ機能が必要な場合に再帰呼び出しにきちんとしたアプローチを提供します-例えば階層オブジェクトなどを構築する場合(明らかに、親関数は1回しか呼び出されないことに注意する必要があります):
function main() {
// Some code
function addChildren ($parentVar) {
// Do something
if ($needsGrandChildren) addChildren ($childVar);
}
addChildren ($mainVar); // This call must be below nested func
// Some more code
}
たとえば、JSと比較したphpの注意点は、ネストされた関数の呼び出しは、関数宣言の後に、つまり以下で行う必要があることです(関数呼び出しが親関数内のどこにでもあるJSと比較して
PHP 7を使用している場合は、次を参照してください: この実装により、ネストされた関数について明確なアイデアが得られます。 関数foo()にネストされた3つの関数(too()、boo()およびzoo())があるとします。 boo()とzoo()には同じ名前のネストされた関数xoo()があります。このコードでは、ネストされた関数のルールを明確にコメントアウトしています。
function foo(){
echo 'foo() is called'.'<br>';
function too(){
echo 'foo()->too() is called'.'<br>';
}
function boo(){
echo 'foo()->boo() is called'.'<br>';
function xoo(){
echo 'foo()->boo()->xoo() is called'.'<br>';
}
function moo(){
echo 'foo()->boo()->moo() is called'.'<br>';
}
}
function zoo(){
echo 'foo()->zoo() is called'.'<br>';
function xoo(){ //same name as used in boo()->xoo();
echo 'zoo()->xoo() is called'.'<br>';
}
#we can use same name for nested function more than once
#but we can not call more than one of the parent function
}
}
/****************************************************************
* TO CALL A INNER FUNCTION YOU MUST CALL OUTER FUNCTIONS FIRST *
****************************************************************/
#xoo();//error: as we have to declare foo() first as xoo() is nested in foo()
function test1(){
echo '<b>test1:</b><br>';
foo(); //call foo()
too();
boo();
too(); // we can can a function twice
moo(); // moo() can be called as we have already called boo() and foo()
xoo(); // xoo() can be called as we have already called boo() and foo()
#zoo(); re-declaration error
//we cannont call zoo() because we have already called boo() and both of them have same named nested function xoo()
}
function test2(){
echo '<b>test2:</b><br>';
foo(); //call foo()
too();
#moo();
//we can not call moo() as the parent function boo() is not yet called
zoo();
xoo();
#boo(); re-declaration error
//we cannont call boo() because we have already called zoo() and both of them have same named nested function xoo()
}
test1()を呼び出すと、出力は次のようになります。
test1:
foo() is called
foo()->too() is called
foo()->boo() is called
foo()->too() is called
foo()->boo()->moo() is called
foo()->boo()->xoo() is called
test2()を呼び出すと、出力は次のようになります。
test2:
foo() is called
foo()->too() is called
foo()->zoo() is called
zoo()->xoo() is called
ただし、再宣言エラーを回避するために、text1()とtest2()の両方を同時に呼び出すことはできません
私はこの特性を実際に使用したのは、プライマリのよりカテゴリ的な関数内で小さな再帰関数を実行するのが有用な場合だけでしたが、プライマリの動作の基本であったため、別のファイルに移動したくありませんでしたプロセス。他の「ベストプラクティス」があることに気づきました。これを行う方法ですが、パーサーを見るたびに開発者がその関数を確認できるようにしたいのですが、とにかく変更する必要があるものです...
ネストされた関数はメモ化に役立ちます(関数の結果をキャッシュしてパフォーマンスを向上させます)。
<?php
function foo($arg1, $arg2) {
$cacheKey = "foo($arg1, $arg2)";
if (! getCachedValue($cacheKey)) {
function _foo($arg1, $arg2) {
// whatever
return $result;
}
$result = _foo($arg1, $arg2);
setCachedValue($cacheKey, $result);
}
return getCachedValue($cacheKey);
}
?>
ネストされた関数は、ネストされた関数で、親関数内で宣言された変数を利用する場合に便利です。
<?php
ParentFunc();
function ParentFunc()
{
$var = 5;
function NestedFunc()
{
global $var;
$var = $var + 5;
return $var;
};
echo NestedFunc()."<br>";
echo NestedFunc()."<br>";
echo NestedFunc()."<br>";
}
?>