В Ruby почему бы не `foo = true, если не определено? (Foo)` сделать присвоение?

Что здесь происходит? Какова тонкая разница между двумя формами "если"?

> irb(main):001:0> foo = true unless defined?(foo)
=> nil 
irb(main):002:0> unless defined?(fooo) ; fooo = false ; end
=> false 

ТНХ

Ответ 1

По-видимому, ruby ​​создает локальную переменную во время синтаксического анализа, устанавливая их в nil, поэтому она определена, и это делается независимо от того, выполняется ли код или нет.

Когда код оценивается в вашей первой строке, он не выполняет часть назначения, так как foo установлен на nil. Во второй строке, поскольку fooo еще не анализируется, defined? возвращает nil, позволяя выполнить код внутри блока и назначая fooo.

В качестве примера вы можете попробовать следующее:

if false  
  foo = 43  
end  
defined? foo  
=> "local-variable"

Это взято из форума post на рубиновом форуме.

Ответ 2

Начнем с чего-то более простого:

# 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 является возвращаемым значением нашего блока.

Ответ 3

irb(main)> foo = true unless defined?(Integer)
=> nil 
irb(main)> foo = true unless defined?(thisIsUndefined)
=> true

Ваш первый блок возвращает nil, потому что способ его записи оставляет 2 варианта:

  • foo не определен → присваивать true
  • foo определяется → ничего не делать

Здесь 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 (в начале строки), даже если вы все еще в процессе его определения.

Ответ 4

в первом случае вы вызываете 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

Ответ 5

В августе все выглядят отлично в 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"

Ответ 6

Ну.. Одна форма - это блок, а одна форма - нет. Вторая часть, блок, возвращает последний обработанный оператор. Первый... Хмм.. Я не знаю точно, что он делает.

Ответ 7

В Ruby 1.8.7:

foo = true unless defined?(foo)
p foo # => nil

unless defined?(fooo); fooo = true; end
p foo # => nil

У меня нет объяснения поведения, которое вы видите.