2016-06-21 5 views
1

私は再帰関数を要するという問題に取り組んでいると、ネストされた実行は、親のパラメータを変更しているように見えることに気づいた:再帰関数には特別な有効範囲規則がありますか?

var foo = function(ar) { 
    console.log('Calling function - Array: ' + ar.toString()); 
    if(ar.length > 1){ 
    var ele = ar.pop(); 
    foo(ar); 
    console.log('Function recursion ends - Array: ' + ar.toString() + ' Popped: ' + ele); 
    return; 
    } else { 
    console.log('Function ends - Array: ' + ar.toString()); 
    return; 
    } 
} 

foo([1,2,3]); 

出力(インデント鉱山):

/* 
Calling function - Array: 1,2,3 
    Calling function - Array: 1,2 
    Calling function - Array: 1 
    Function ends - Array: 1 
    Function recursion ends - Array: 1 Popped: 2 
Function recursion ends - Array: 1 Popped: 3 <-- What happened to 2? 
*/ 

私は[1,2,3]で関数を呼び出しているので、これは奇妙に思えます。そして、関数の最初の反復では、両方の要素の間に渡された要素のすべてを維持することが期待されます。arele - その代わりに、関数が終了すると、指定された配列には1しか残りません。2はどうなりましたか?ネストされた実行popは最初の実行の変数から外しましたか?

JavaScriptで関数スコープの私の理解では、関数に渡された変数はローカルでのみそれらを変更することができ、ここで見られるように、グローバル/親の範囲に戻ってそれらをエクスポートしていないと言うでしょう:

var bar = 'bar'; 

function doBar(bar){ 
bar = 'foo'; 
} 

doBar(bar); 
console.log(bar); //Outputs 'bar'; 

しかし、出力再帰関数からその理解に挑戦するようだ。

これらのネストされた実行が親のパラメータを変更して、不足している2を戻さないようにするにはどうすればよいですか? JavaScriptのスコープの理解が間違っていますか?


この質問を開く前にストローで把握する私の哀れな試みで、私はクロージャ内の関数を実行しようとしました:

var foo = function(ar) { 
    console.log('Calling function - Array: ' + ar.toString()); 
    if(ar.length > 1){ 
    var ele = ar.pop(); 
    (function(foo, ar){ 
     foo(ar); 
    })(foo, ar) 

    console.log('Function recursion ends - Array: ' + ar.toString() + ' Popped: ' + ele); 
    return; 
    } else { 
    console.log('Function ends - Array: ' + ar.toString()); 
    return; 
    } 
} 

しかし、私は閉鎖を使用せずに同じ結果を得ました - 私は明らかにarfooで渡されたので、それは閉鎖なしと変わらないものであると思う。

+0

入れ子になったのfoo()あなたが何かをログに記録する前に、アレイ上で実行されます。入れ子にされた 'foo()'の前にロギングを試みてください。 – charlietfl

+3

いいえ、再帰について特別なことは何もなく、これはスコープとは関係ありません。すべての呼び出しは独自の 'ar'変数を作成します。彼らはすべて同じ配列オブジェクトを参照し、 'pop'はそれを突然変異させるだけです。 – Bergi

+0

彼らはあなたが「Function recursion ends ...」のロギングを始める時に、コンソールがあなたに何を伝えているかを与えられていますか? – JonSG

答えて

1

は、変更可能な操作です。 fooを再帰的に呼び出すと、その変更可能な操作は依然として呼び出しスコープ内で有効です。範囲外で奇妙なことが起こることはありません。foo内の操作では、指定したパラメータの内部を変更できないと予想しているだけです。配列は参照によって渡されます。つまり、パラメータは呼び出しスコープと同じ配列を参照します。

代わりにarr.slice(0, -1)に電話することができます。これは、既存の配列を変更するのではなく、配列の浅いコピーを返します。配列の最後のインデックスの取得方法が変更されます。

var foo = function(ar) { 
 
    console.log('Calling function - Array: ' + ar.toString()); 
 
    if(ar.length > 1){ 
 
    var ar2 = ar.slice(0, -1); 
 
    foo(ar2); 
 
    console.log('Function recursion ends - Array: ' + ar2.toString() + ' Popped: ' + ar.slice(-1)); 
 
    return; 
 
    } else { 
 
    console.log('Function ends - Array: ' + ar.toString()); 
 
    return; 
 
    } 
 
} 
 

 
foo([1,2,3]);

+2

あなたは、「関数に渡された変数はローカルでしか変更できません」というOPの期待に対処していません。その答えは、各呼び出しの 'ar'引数が*同じ配列を参照することを明示的に述べるべきです。この質問の下で直接コメントされたように、これは再帰またはスコープに関するものではなく、配列のコピーを作成しない配列参照に関するものです。 – nnnnnn

+0

"あなたは' foo'内の操作が与えたパラメータを変更できないと予想しているかもしれません。ブーム。すぐそこに。つまり、配列の最後の要素を削除することは、関数の動作の意図された部分でした。だから問題が解決したときに配列をコピーすることは、より良い解決策でした。 – HPierce

+0

@nnnnnn私はそれに言及しましたが、今私はそれを答えに明示しました。とにかく十分に質問に答えてくれてうれしいです。 – 4castle

0

4castleが示唆@として、あなたは、各反復で配列のより耐久性のあるビューを持つようにしたい場合は、あなたは)(スライスとarを複製することができます。あるいは、関数foo()を再実行して、 "現在の" arとeleで作業する余地を増やすこともできます。

var foo = function(ar) { 
 
    console.log('Given Array: ' + ar.toString()); 
 
    
 
    if(ar.length === 0){ 
 
    console.log('recursion ended'); 
 
    return; 
 
    } 
 
    
 
    var ele = ar.pop(); 
 
    console.log('Array Now: ' + ar.toString() + ' After Popping: ' + ele); 
 
    console.log(" "); 
 

 
    foo(ar); 
 
} 
 

 
foo([1,2,3]);

関連する問題