2017-01-03 2 views
0

Stellen Sie sich das folgende Problem vor: Ich möchte ein Array von Funktionen erstellen, jede Funktion druckt nur ihren Index in diesem Array. In Python kann es verwenden wir Standardargument Werte als eine Möglichkeit, leicht mitStandardparameter und currying: Python vs. Javascript

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

hier getan werden, um zu tun currying (wenn ich den Begriff richtig verstehen).

Vor ES6 gab es in Javascript keine Standardargumentwerte, daher musste das Curry auf andere Weise durchgeführt werden. Jetzt haben wir sie und ich habe versucht, Python ins Javascript wörtlich zu übersetzen:

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) 

Warum es scheitert? Was ist der Unterschied zwischen Python und Javascript Möglichkeiten, um Standardwerte zu übergeben?

(Okay, ich weiß, dass ich let hier statt var verwenden können, ich studiere nur Javascript nach mehreren Jahren mit Python und versuchen, es underhoods zu verstehen.)

+0

Verwenden Sie einfach einen anderen Variablennamen, 'j = i' dann' console.log (j) ' – elclanrs

+1

Dies hat nichts mit Currying zu tun. Dies hat mit der späten Bindung von Closures in Python zu tun. Dies ist ein Hack, der die Tatsache ausnutzt, dass die Standardargumente in Python zur Zeit der Funktionsdefinition gesetzt sind, so dass Sie die frühe Bindung simulieren können. Wahrscheinlich liegen da die Unterschiede mit Javascript (das meiner Meinung nach auch spätbindenden Schließungen hat). –

+0

@elclanrs das Ändern des Variablennamens stellt einfach ein anderes Problem dar, das ist die Schließung, die durch die for-Schleife erzeugt wird, in der 'funcs [n]' immer den maximalen Wert von 'i' zurückgibt. Auf was juanpa.arrivillaga mit späten vs simulierten frühen Bindung in Schließungen anspielte. – mhodges

Antwort

1

Sowohl Javascript als auch Python mit Late-Binding in Verschlüsse.Das Verwenden eines Standardarguments ist jedoch ein Hack, mit dem Sie die frühe Bindung in Python simulieren können, und das funktioniert, weil Standardparameter bei Funktionsdefinitionszeit in Python ausgewertet werden. in Javascript Standardargumenten jedoch nach dem docs

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

Das Standardargument wird bei Anrufzeit ausgewertet, so im Gegensatz zu z in Python, jedes Mal, wenn die Funktion aufgerufen wird, wird ein neues Objekt erstellt.

Hier ist eine der in Python verwendeten Lösungen, die auf dieses Problem in Javascript angewendet werden können. Ich habe in Javascript so gut wie möglich übersetzt. Definieren Sie im Wesentlichen eine andere anonyme Funktion, die Ihr Original zurückgibt und es gleichzeitig anwendet. Dies ist sehr chaotisch, um meine Augen, und in Python, gehe ich immer mit dem Standard-Argumente:

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

In Python:

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

Ich denke, es ist klar, dass Sie .bind in Javascript verwenden sollten , wie in anderen Antworten erläutert, und nicht diese klobige Lösung.

+0

Keine Notwendigkeit, Funktionen zu verschachteln, verwenden Sie '.bind()' genau so mit einer Inline-Funktion 'funcs.push ((function (i) {console.log (i);}). Bind (this, i)); ' – mhodges

+1

Ich stimme dir zu, dass du' .bind' verwenden solltest, ich bin kein Javascript-Programmierer, obwohl ich daran herumgebastelt habe und keine Kommentare zu Best Practices abgeben kann, ich wollte nur verdeutlichen, was gerade passiert Python. –

2

Ihre Probleme mit dem Unterschied, zu tun haben, zwischen wennStandard Parameter werden in Schließungen in Python vs JavaScript gebunden. Es ist zwar richtig, dass sowohl JavaScript als auch Python eine späte Bindung verwenden. Im Fall von Standardparametern simuliert Python die frühe Bindung, JavaScript dagegen nicht.

Das heißt, wenn Sie Schließungen wie diese erstellen, können Sie auch Nutzen aus ihnen ziehen und die Parameter alle zusammen, ehrlich.

Sie erwähnten die Verwendung von let und das ist wichtig, wenn Sie die Funktion innerhalb der for-Schleife definieren möchten, da sonst funcs[n] immer der Maximalwert Ihres Iterators ist (aufgrund der späten Bindung von JavaScript-Verschlüsse).

Versuchen Sie folgendes:

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

Alternativ, wenn Sie eine gute Praxis nicht definieren Funktionen innerhalb einer Schleife folgen wollen, können Sie die Funktion außerhalb, definieren und die Variable übergeben Sie sich mit der .bind(). Eine Sache zu beachten ist, dass diese Methode wird die Variable mit dem Wert zum Zeitpunkt bindet .bind() genannt wird, so dass Sie nicht let

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

+0

Nur um klar zu sein, ist die Bindung von freien Variablen spät in Python und Javascript jedoch dort ist eine Eigenart in Python, dass Standardargumente zur Funktionsdefinitionszeit gesetzt werden, so dass Sie die späte Bindung umgehen können. –

+0

@ juanpa.arrivillaga Danke, das habe ich in meinem Originalbeitrag nicht so deutlich gemacht - ich habe es entsprechend aktualisiert. – mhodges

+0

Danke! Ich schätze '.bind' Lösung. Ich würde diese Antwort auch gerne akzeptieren, wenn ich mehrere Antworten akzeptieren könnte ... –

1

verwenden Wenn Sie let anstelle von var Sie erhalten eine neue Bindung für jede Iteration.

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

funcs[2];//2 

i=i nicht funktioniert, weil in ES6 Parameter Standardwerte unter Verwendung anderer Parameter

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

So der Parser verwirrt wird immer definieren.

+0

Danke, ich mag den Punkt auf * 'i = i' funktioniert nicht, da Parameter in ES6 Standardwerte mit anderen Parametern definieren können *. Es beantwortet meine Antwort teilweise, es ist schade, dass ich zwei Antworten nicht simultan annehmen kann. –