В Ruby почему `foo = true, если не определено? (Foo)` не выполняет назначение? - PullRequest
14 голосов
/ 18 февраля 2010

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

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

ТНХ

Ответы [ 7 ]

16 голосов
/ 19 февраля 2010

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

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

В качестве примера вы можете попробовать это:

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

Это взято с форума сообщение на ruby-forum.

12 голосов
/ 19 февраля 2010

Давайте начнем с чего-то более простого:

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

2 голосов
/ 18 февраля 2010
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) самостоятельно).Попробуйте использовать другое имя и посмотрите, получите ли вы те же результаты.

Редактировать:

irb(main)> defined?(bar)
=> nil
irb(main)> bar = true unless defined?(bar)
=> nil
irb(main)> defined?(bar)
=> "local-variable"

По-видимому, defined?() возвращает значение true, поскольку он уже виделbar (в начале строки), даже если вы все еще находитесь в процессе его определения.

1 голос
/ 27 мая 2010

Август, все выглядит нормально в 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"
1 голос
/ 18 февраля 2010

в первом случае вы вызываете 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
0 голосов
/ 18 февраля 2010

В Ruby 1.8.7:

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

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

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

0 голосов
/ 18 февраля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...