2010-06-06 3 views
7

Когда a не определено, тогда a || 1 выдает ошибку, но a = a || 1 не будет. Разве это не немного непоследовательно?Почему в Ruby, a || 1 будет вызывать ошибку, когда `a` не определено, но a = a || 1 не будет?

irb(main):001:0> a 
NameError: undefined local variable or method 'a' for main:Object 
     from (irb):1 
     from c:/ruby/bin/irb:12:in '<main>' 

irb(main):002:0> a || 1 
NameError: undefined local variable or method 'a' for main:Object 
     from (irb):2 
     from c:/ruby/bin/irb:12:in '<main>' 

irb(main):003:0> a = a || 1 
=> 1 
+0

Дубликат http://stackoverflow.com/questions/1462407/ruby-edge-cases –

ответ

9
a 

Здесь вы оцениваете a, что не определено. Поэтому вы получаете исключение.

a || 1 

Здесь вы еще должны оценить a определить значение логического выражения. Как и выше, a не определен. Поэтому вы получаете исключение.

a = a || 1 

Здесь aявляется определена. Он определяется как неинициализированная локальная переменная. В Ruby, неинициализированные переменные вычисляются в nil, так что правая часть выражения присваивания вычисляется в nil || 1, который принимает значение 1, так что возвращаемое значение выражения присваивания является 1 и побочный эффект заключается в том, что a инициализируется 1.

EDIT: Кажется, что есть некоторая путаница, когда переменные определяются и когда они инициализируются в Ruby. Получаемое значение определено в Время разбора, но инициализировано на время выполнения. Вы можете увидеть здесь:

foo # => NameError: undefined local variable or method `foo' for main:Object 

foo не определен.

if false 
    foo = 'This will never get executed' 
end 

На данный момент, foo определяется, даже если линия никогда не будет выполнена. Тот факт, что строка никогда не выполняется, совершенно не имеет значения, потому что интерпретатор не имеет ничего общего с этим: локальные переменные определяются синтаксическим анализатором, и синтаксический анализатор явно видит эту строку.

foo # => nil 

Там нет ошибки, потому что foo определена, и она принимает значение nil, потому что это неинициализированное.

+0

+1, хотя я думаю, что «Он определен как неинициализированная локальная переменная». немного запутанно. Когда вы выполняете 'var = expr', вы не определяете' var' для неинициализации. Вы определяете var как значение 'expr'. Это просто, что, когда выражение 'expr' оценивается,' var' оказывается неинициализированным, это означает, что любые ссылки на 'var' внутри' expr' будут вычисляться на nil. – sepp2k

+1

@ sepp2k: Это два отдельных шага. Определение переменной происходит в синтаксическом анализаторе, инициализация в интерпретаторе. В зависимости от реализации Ruby между двумя событиями может быть значительное время. Например, в BlueRuby исходный код Ruby обрабатывается в BRIL (промежуточный язык BlueRuby), который затем сохраняется в базе данных, когда вы * устанавливаете * программу Ruby. Это может быть * лет *, пока кто-то на самом деле не запустит программу. Все это время переменная была определена, но не была инициализирована. Две вещи могут произойти на разных машинах. –

1

Когда вы a || 1, вы просите его искать значения a которое не определено.

Когда вы делаете a = a || 1, вы просите его найти значение присвоения aa, которое, кажется, не дает ошибку.

Итак, хотя это странно, я не считаю, что это противоречиво.

+0

Это неправильно.'a = a || 1' не анализируется как '(a = a) || 1'. Он анализирует как 'a = (a || 1)', поэтому результат «присвоения a a» не имеет к этому никакого отношения, поскольку a никогда не присваивается a. – sepp2k

+0

@ sepp2k: Я не пытался это подразумевать. Извините, если я смутил вас. Я считаю, что Йорг объяснил это более четко. – Cetra

0

Это вы имеете в виду?

if !(defined? a) then 
    a = 1 
end 

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