Удивительный рубиновый обзор с циклом 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
Другие вопросы по тегам