Удивительный рубиновый обзор с циклом while
(1)
a = [1, 2]
while b = a.pop do puts b end
выходы
2
1
(2)
a = [1, 2]
puts b while b = a.pop
приводит к ошибке
undefined local variable or method `b'
(3)
b = nil
a = [1, 2]
puts b while b = a.pop
выходы
2
1
Что здесь происходит? Почему сфера b
отличается #2 чем-либо из остальных?
$ ruby --version
ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-linux]
РЕДАКТИРОВАТЬ: Первоначально я перечислил поведение IRB как разные. Это не так; Я работал на "грязной" сессии.
3 ответа
Переменные объявляются в их область видимости лексическим парсером, который является линейным. В while b = a.pop do puts b end
, назначение (b = a.pop
) видится парсером перед использованием (puts b
). Во втором примере puts b while b = a.pop
, использование замечено, когда определение все еще неизвестно, которое производит ошибку.
Оператор put выполняется до того, как переменная 'b' была первоначально определена, что приводит к ошибке.
В качестве аналогичного примера, но с оператором till, рассмотрим следующий код:
a = [1, 2]
begin
puts "in the block"
end until b = a.pop
Вы ожидаете, что b будет определен в блоке?
Технически единственная разница в том, что before останавливается на возвращаемом значении true, а while будет продолжаться до тех пор, пока a.pop возвращает истинное значение.
Смысл в обоих случаях заключается в том, что b не находится в области действия, пока не произошло присвоение Сразу после назначения, например, когда цикл возвращается, b становится доступным в текущей области. Это называется лексической областью видимости и именно так работает ruby для локальных переменных, подобных этой.
Я нашел эту статью полезной для понимания объема в ruby.
Обновление 1: в предыдущей версии моего ответа я писал, что это несопоставимо с if. Хотя это все еще верно, это не имеет ничего общего с вопросом, который является простой проблемой.
Обновление 2: Добавлена ссылка на некоторые более подробные пояснения относительно определения объема в рубине.
Обновление 3: удалено первое предложение, так как оно было неверным.
a = [1, 2]
while b = a.pop do puts b end
такой же как
a = [1, 2]
b = a.pop
puts b
b = a.pop
puts b
(2)
a = [1, 2]
puts b while b = a.pop
такой же как этот
a = [1, 2]
puts b
b = a.pop
puts b
b = a.pop
puts b
Когда b передается в пут, первый раз он еще не был инициализирован. отсюда сообщение об ошибке
(3) b инициализируется нулем. даже ноль - объект в рубине
b = nil
a = [1, 2]
puts b while b = a.pop