Что здесь происходит? Какова тонкая разница между двумя формами "если"?
> irb(main):001:0> foo = true unless defined?(foo)
=> nil
irb(main):002:0> unless defined?(fooo) ; fooo = false ; end
=> false
ТНХ
Что здесь происходит? Какова тонкая разница между двумя формами "если"?
> irb(main):001:0> foo = true unless defined?(foo)
=> nil
irb(main):002:0> unless defined?(fooo) ; fooo = false ; end
=> false
ТНХ
По-видимому, ruby создает локальную переменную во время синтаксического анализа, устанавливая их в nil
, поэтому она определена, и это делается независимо от того, выполняется ли код или нет.
Когда код оценивается в вашей первой строке, он не выполняет часть назначения, так как foo
установлен на nil
. Во второй строке, поскольку fooo
еще не анализируется, defined?
возвращает nil
, позволяя выполнить код внутри блока и назначая fooo
.
В качестве примера вы можете попробовать следующее:
if false
foo = 43
end
defined? foo
=> "local-variable"
Это взято из форума post на рубиновом форуме.
Начнем с чего-то более простого:
# z is not yet defined
irb(main):001:0> defined?(z)
=> nil
# Even though the assignment won't execute,
# the mere presence of the assignment statement
# causes z to come to life.
irb(main):002:0> z = 123 if false
=> nil
irb(main):003:0> defined?(z)
=> "local-variable"
irb(main):004:0> z
=> nil
Теперь мы можем выяснить ваш первый пример.
foo = true unless defined?(foo)
Определен ли foo
? Прежде чем нажимать ENTER в irb
, нет. Однако наличие оператора присваивания приводит к оживлению foo
. Это означает, что оператор присваивания не будет выполняться, оставив foo
, но имеющий nil
как его значение. И каково последнее выражение, оцененное в строке irb
? Это unless defined?(foo)
, который оценивается как nil
.
Для получения дополнительной информации о том, как назначения (даже те, которые не выполняются) приводят к появлению переменных, см. это обсуждение Неопределенность переменных/методов.
В вашем втором примере ничего загадочного нет: fooo
не определен, поэтому код в блоке выполняется, установив fooo
в false
. Это присваивание является последним выраженным выражением, поэтому false
является возвращаемым значением нашего блока.
irb(main)> foo = true unless defined?(Integer)
=> nil
irb(main)> foo = true unless defined?(thisIsUndefined)
=> true
Ваш первый блок возвращает nil
, потому что способ его записи оставляет 2 варианта:
foo
не определен → присваивать truefoo
определяется → ничего не делатьЗдесь foo должен быть определен при вычислении строки. Таким образом, ничего не происходит, и возвращается nil
.
irb(main)> unless defined?(Integer) ; fooo = false ; end
=> nil
irb(main)> unless defined?(thisIsUndefined) ; fooo = false ; end
=> false
Второй блок работает так же, как и ваш первый. Если fooo
не определено, блок вводится, а fooo
- false
. Результатом последней строки блока является возвращаемое значение блока, таким образом, false
, которое вы видите. Если fooo
существует, то блок пропускается и ничего не происходит, поэтому возвращаться нечего, поэтому nil
.
Основываясь на вашем коде, я бы сказал, что foo
был определен, когда этот код был запущен, а fooo
не был (тестовый код показан в Ruby 1.8.6). Если вы не указали ни один из этих параметров перед запуском этого кода, то вы можете иметь что-то под названием foo
, которое определено по умолчанию (сначала выполните defined?(foo)
, чтобы проверить). Попробуйте использовать другое имя и посмотрите, получаете ли вы те же результаты.
Edit:
irb(main)> defined?(bar)
=> nil
irb(main)> bar = true unless defined?(bar)
=> nil
irb(main)> defined?(bar)
=> "local-variable"
По-видимому, defined?()
возвращает true, так как он уже видел bar
(в начале строки), даже если вы все еще в процессе его определения.
в первом случае вы вызываете foo
в существование в операторе присваивания. Возможно, это прояснит:
bar = if true
puts bar.class
else
puts "not reached"
end
NilClass
=> nil
baz = if true
puts baz.class
42
else
puts "not reached"
end
NilClass
=> 42
В августе все выглядят отлично в 1.8.7:
$ irb
irb(main):001:0> unless defined?(fooo); fooo = true; end
=> true
irb(main):002:0> fooo
=> true
irb(main):003:0> `ruby --version`
=> "ruby 1.8.7 (2008-06-20 patchlevel 22) [i486-linux]\n"
Ну.. Одна форма - это блок, а одна форма - нет. Вторая часть, блок, возвращает последний обработанный оператор. Первый... Хмм.. Я не знаю точно, что он делает.
В Ruby 1.8.7:
foo = true unless defined?(foo)
p foo # => nil
unless defined?(fooo); fooo = true; end
p foo # => nil
У меня нет объяснения поведения, которое вы видите.