2017-01-03 5 views
0

次の問題を考えてみましょう。各関数は配列のインデックスを出力するだけで、関数の配列を作成します。 Pythonでは、それはここでは(私は右の用語を理解していれば)、カリー化を行うための方法として、デフォルト引数値を使用しデフォルトのパラメータとカリング:Python対Javascript

funcs = [] 
for i in range(5): 
    funcs.append(lambda i=i: print(i)) 
funcs[2]() 
# 2 

で簡単に行うことができます。

ES6以前では、Javascriptにデフォルトの引数値がなかったため、カリングは別の方法で行われなければなりませんでした。今、私たちはそれらを持っていると私は文字通りJavaScriptにはPythonを翻訳してみました:

funcs = [] 
for (var i=0; i<5; i++) { 
    funcs.push(function (i=i) {console.log(i)}) 
} 
# this part pass OK 
funcs[2]() 
ReferenceError: i is not defined 
    at Array.<anonymous> (evalmachine.<anonymous>:3:27) 
    at evalmachine.<anonymous>:1:9 
    at ContextifyScript.Script.runInThisContext (vm.js:26:33) 
    at Object.exports.runInThisContext (vm.js:79:17) 
    at run ([eval]:608:19) 
    at onRunRequest ([eval]:379:22) 
    at onMessage ([eval]:347:17) 
    at emitTwo (events.js:106:13) 
    at process.emit (events.js:191:7) 
    at process.nextTick (internal/child_process.js:752:12) 

それが失敗したのはなぜ?デフォルト値を渡すPythonとJavascriptの違いは何ですか?

(さて、私は私が代わりにvarの、私はただのPythonと、数年後にJavascriptを勉強し、それがunderhoods理解しようとしているここletを使用することができることを知っている。)

+0

'j = i'、' console.log(j) ' – elclanrs

+1

という別の変数名を使うだけです。これはカリングとは関係ありません。これは、Pythonでのクロージャの遅延バインディングと関係があります。実際には、Pythonのデフォルト引数が関数定義時にセットされ、初期バインディングをシミュレートできるという事実を利用するハックです。おそらく、Javascriptとの違いがあります(私はこれもlate-binding closureを持っていると信じています)。 –

+0

@elclanrsは変数名を変更するだけで、forループによって作成されるクロージャで、 'funcs [n]'は常に 'i'の最大値を返します。これは、juanpa.arrivillagaが、後期とシミュレートされた早期の結束で結びついていたものです。 – mhodges

答えて

1

JavascriptとPythonの両方で、クロージャーでレイトバインディングを使用しています。しかし、既定の引数を使用すると、Pythonでの初期バインディングをシミュレートできるハックがあります。これは、既定のパラメータがの関数定義時にPythonので評価されるために機能します。しかし、Javascriptのデフォルト引数で、ドキュメント

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters

に応じてデフォルトの引数は、呼び出し時に評価などとは違ってとてもましますin Pythonでは、関数が呼び出されるたびに新しいオブジェクトが作成されます。

ここは、JavaScriptでこの問題に適用できるPythonで使用されるソリューションの1つです。私は可能な限り最高のJavascriptに翻訳しました。基本的に匿名関数を定義し、元の値を返し、同時に適用します。これが私の目には非常に厄介であり、Pythonで、私は常にデフォルトの引数と一緒に行く:

Pythonで
funcs = [] 
for (var i = 0; i < 5; i++) { 
    funcs.push((function (i) {return function() {console.log(i)}})(i)) 
}; 
funcs[0]() // 0 
funcs[4]() // 4 

>>> funcs = [] 
>>> for i in range(5): 
...  funcs.append((lambda i: lambda : print(i))()) 
... 
>>> funcs[0]() 
0 
>>> funcs[4]() 
4 

私はあなたがJavaScriptで.bindを使用する必要があることは明らかだと思います、他の答えで解明されているように、そしてこの厄介な解決策ではない。

+0

関数をネストする必要はなく、インライン関数 'funcs.push(function(i){console.log(i);})と同様に' .bind() 'を使うだけです。 ' – mhodges

+1

' .bind'を使うべきだと私は同意します。私はそれを使いこなしていますが、ベストプラクティスについてコメントすることはできませんが、私はJavascriptプログラマーではありません。私はちょうどそれをPython。 –

2

あなたの問題は、違いとしなければなりませんの場合デフォルトパラメータがPythonとJavaScriptのクロージャでバインドされます。 JavaScriptとPythonの両方が遅延バインディングを使用するのは当然ですが、デフォルトパラメータの場合、Pythonはアーリーバインディングをシミュレートしますが、JavaScriptはそうではありません。

このようにクロージャを作成する場合は、それらを利用して、正直なところ、すべてのパラメータをまとめて処理することもできます。

letの使用を言及しました。そうでないと、funcs[n]が(JavaScriptクロージャのレイトバインディングのために)イテレータの最大値になるため、forループ内で関数を定義したい場合は重要です。

これを試してみてください:

funcs = []; 
 
for (let i=0; i<5; i++) { 
 
    funcs.push(function() {console.log(i)}); 
 
} 
 
funcs[2]();

を別の方法として、あなたはループ内の関数を定義していないのは良い習慣をフォローしたい場合は、外部の関数を定義し、使用して変数を渡すことができます.bind()。もう一つ注意すべきは、あなたがlet代わりのvarを使用する場合はlet

funcs = []; 
 
function myFunc(i) { 
 
    console.log(i); 
 
} 
 
for (var i=0; i<5; i++) { 
 
    funcs.push(myFunc.bind(this, i)); 
 
} 
 
funcs[2]();

+0

フリー変数のバインドは、PythonとJavascriptの両方で遅いです。 Pythonでは、関数の定義時にデフォルトの引数が設定されているため、遅延バインドを回避できます。 –

+0

@ juanpa.arrivillagaありがとう、私は私の元の投稿でそれを非常に明確にしませんでした - 私はそれに応じて更新しました。 – mhodges

+0

ありがとう!私は '.bind'の解決に感謝します。 –

1

を使用する必要はありませんので、このメソッドは、呼び出された時.bind()の値を持つ変数を結合することです各反復ごとに新しいバインディングを取得します。

funcs = [] 
for (let i=0; i<5; i++) { 
    funcs.push(function (j=i) {console.log(j)}) 
} 

funcs[2];//2 

i=iがあるためES6パラメータでは動作しませんので、パーサが混乱してきている他のパラメータ

f = function(a=1, b=a){console.log(b);} 
f() // 1 

を使用してデフォルトを定義することができます。

+0

ありがとう、私は本当にポイントが好きです* ES6のパラメータでは、他のパラメータを使ってデフォルトを定義できるので、 'i = i'は動作しません。それは部分的に私の答えに答える、私は2つの答えをsimulatniously受け入れることができない同情だ。 –

関連する問題