Закрытия и для циклов в Ruby

Я довольно плохо знаком с Ruby, и часть логики закрытия имеет меня запутанное. Рассмотрите этот код:

array = []
for i in (1..5)
  array << lambda {i}
end
array.map{|f| f.call} # => [5, 5, 5, 5, 5]

Это имеет смысл мне, потому что я связываюсь вне цикла, таким образом, та же переменная получена каждым прохождением через цикл. Это также имеет смысл мне, что использование каждого блока может зафиксировать это:

array = []
(1..5).each{|i|  array << lambda {i}}
array.map{|f| f.call} # => [1, 2, 3, 4, 5]

... потому что я теперь объявляюсь отдельно в течение каждого раза через. Но теперь я заблудился: почему я не могу также зафиксировать его путем представления промежуточной переменной?

array = []
for i in 1..5
  j = i
  array << lambda {j}
end
array.map{|f| f.call} # => [5, 5, 5, 5, 5]

Поскольку j является новым каждый раз через цикл, я думал бы, что другая переменная будет получена на каждой передаче. Например, это определенно, как C# работает, и как - я думаю - Lisp ведет себя с позволенным. Но в Ruby не так. Что действительно происходит?

Править: См. комментарии в ответах; проблема, кажется, что j находится все еще в объеме вне цикла. Как действительно определяет объем в циклах, действительно работают?

Править: Я предполагаю, что все еще не понимаю; если циклы не создают новые объемы, почему это:

for i in 1..5
  puts j if i > 1 #undefined local variable or method `j' for main:Object (NameError)
  j = i
end
5
задан 26 July 2011 в 20:27
поделиться

2 ответа

Ладно, это уже смешно. Каждый раз, когда я пытаюсь ответить на вопрос о том, как циклы for работают в Ruby, я ошибаюсь.

Причина этого, конечно же, в том, что я не использую для циклов в Ruby, как и никто другой, так что для меня это действительно не имеет значения: -)

В любом случае , чтобы решить этот вопрос раз и навсегда, я сразу обратился к первоисточнику , предварительному проекту от 1 декабря 2009 г. спецификации языка Ruby IPA (призванной стать спецификацией языка ISO Ruby):

§11.4.1.2.3 для выражение

Синтаксис

  • для-выражения для для-переменной в выражение do-clause end
  • for-variable левая сторона | множественная левая сторона

выражение из выражения for не должно быть выражением перехода .

Семантика

A for-expression оценивается следующим образом:

  1. Вычислить выражение . Пусть O будет результирующим значением.
  2. Пусть E будет вызовом-первичным методом формы первичным-выражением [здесь нет ограничителя строки] . каждый do | список-формальных-аргументов блока | тело-блока конец , где значение первичного-выражения равно O , список-формальных-аргументов блока - это для переменной , тело блока - это составной оператор ] из do-clause .

    Оцените E , но пропустите шаг c из §11.2.2.

  3. Значение выражения for является результирующим значением вызова.

Хорошо, в основном это означает, что

for for_variable in expression
  do_clause
end

переводится в

O = expression
O.each do |for_variable|
  do_clause
end

Или, в вашем случае:

for i in 1..5
  puts j if i > 1 #undefined local variable or method `j' (NameError)
  j = i
end

переводится в

(1..5).each do |i|
  puts j if i > 1 #no excpetion here, works just fine ??!!??
  j = i
end

Ага! Но мы кое-что забыли! Это зловещее «пропустить шаг c из §11.2.2». вещь! Итак, что он говорит?

  • Вставьте пустой набор привязок локальных переменных в «привязки локальных переменных».

Обратите внимание, что на этапе b

  • контекст выполнения устанавливается равным E b .

- это , а не пропущено.

Итак, насколько я понимаю, цикл for получает свой собственный контекст выполнения, который начинается как копия текущего контекста выполнения, но он не получает собственный набор привязок локальных переменных. IOW: он получает свой собственный динамический контекст выполнения, но не собственную лексическую область видимости.

Должен признать, я все еще не уверен, что полностью понимаю это, но нет более точного, чем это.

9
ответ дан 13 December 2019 в 22:03
поделиться

На какой версии Ruby вы это делаете? В 1.8 нет области видимости блока для локальных переменных, поэтому j все еще висит (и равна 5) даже после окончания for.

1
ответ дан 13 December 2019 в 22:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: