Застежки и петли в рубине - PullRequest
5 голосов
/ 14 мая 2010

Я немного новичок в 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 ведет себя с let. Но в 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

Ответы [ 2 ]

9 голосов
/ 14 мая 2010

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

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

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

§11.4.1.2.3 for выражение

Синтаксис

  • для выражения & rarr; для для переменной в выражение do-clause end
  • для переменных & rarr; слева | несколько слева

Выражение для выражения for не должно быть выражением скачка .

Семантика

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

  1. Оценить выражение . Пусть O будет результирующим значением.
  2. Пусть E будет вызовом первичного метода формы первичное выражение [здесь нет конца строки] .each do | block-формальный-аргумент-список | block-body end , где значение primary-expression - O, block-формальный-аргумент-список - for-variable , block-body является составным оператором из do-clause .

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

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

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

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

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

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

Обратите внимание, что Шаг b

  • Установить контекст выполнения на Eb.

это не пропущено.

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

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

1 голос
/ 14 мая 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...