2017-01-03 12 views
0

Рассмотрим следующую проблему: я хочу создать массив функций, каждая функция просто печатает свой индекс в этом массиве. В Python, это можно легко сделать сПараметры по умолчанию и currying: Python vs. Javascript

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

Здесь используются значения по умолчанию аргумент, как способ сделать выделки (если я понимаю этот термин справа).

До ES6 в Javascript не было значений аргументов по умолчанию, поэтому каррирование должно выполняться по-разному. Теперь у нас есть они, и я попытался перевести Python на Javascript буквально:

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 для передачи значений по умолчанию?

(Хорошо, я знаю, что я могу использовать let здесь вместо var, я просто изучать Javascript после нескольких лет с Python и пытаюсь понять это underhoods.)

+0

Просто используйте другое имя переменной, 'j = i' then' console.log (j) ' – elclanrs

+1

Это не имеет никакого отношения к currying. Это связано с поздним связыванием замыканий в Python. На самом деле это хак, который использует тот факт, что аргументы по умолчанию в Python устанавливаются во время определения функции, позволяя имитировать раннее связывание. Вероятно, есть различия с Javascript (который, я считаю, также имеет поздние связывание). –

+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

аргумент по умолчанию получает оценку во время вызова, так что в отличие, например, в Python, каждый новый объект создается каждый раз при вызове функции.

Это одно из решений, используемых в Python, которые могут быть применены к этой проблеме в Javascript. Я транслировал в Javascript все, что мог. По существу, определите еще одну анонимную функцию, которая возвращает ваш оригинал и применит его одновременно. Это очень грязно в моих глазах, и в Python, я всегда иду с Default-аргументе:

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

В Python:

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

Я думаю, ясно, что вы должны использовать .bind в Javascript , как выяснено в других ответах, а не это неуклюжее решение.

+0

Нет необходимости встраивать функции, просто используйте '.bind()', как это, с встроенной функцией 'funcs.push ((function (i) {console.log (i);}). Bind (this, i)); ' – mhodges

+1

Я согласен, что вы должны использовать' .bind', я не программист Javascript, хотя я потрудился с ним и не могу комментировать лучшие практики, я просто хотел выяснить, что происходит, сравнивая его с Python. –

2

ваших вопросов должны сделать с той разницей, между , когдапо умолчанию параметры связаны с закрытием в python и JavaScript. Хотя верно, что JavaScript и Python используют позднюю привязку, в случае параметров по умолчанию Python имитирует раннее связывание, тогда как JavaScript не работает.

Если вы собираетесь создавать подобные замыкания, вы можете также воспользоваться ими и честно оценить параметры.

Вы упомянули использование let, и это важно, если вы хотите определить функцию внутри цикла for, потому что иначе funcs[n] всегда будет вашим максимальным значением вашего итератора (из-за позднего связывания закрытия JavaScript).

Попробуйте это:

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

В качестве альтернативы, если вы хотите следовать хорошей практике не определения функций в цикле, вы можете определить функцию снаружи, и передать переменную в использовании .bind(). Единственное, что следует отметить, что этот метод будет связать переменную со значением в то время .bind() называется, так что вы не должны использовать 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

Если вы используете let вместо var вы получаете новое привязку для каждой итерации.

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

Спасибо, мне очень нравится точка * 'i = i' не работает, потому что в ES6 параметры могут определять значения по умолчанию с использованием других параметров *. Он частично отвечает на мой ответ, жаль, что я не могу принять два ответа симулятивно. –

 Смежные вопросы

  • Нет связанных вопросов^_^