In Ruby why won't `foo = true unless defined?(foo)` make the assignment?

后端 未结 7 922
悲哀的现实
悲哀的现实 2020-12-03 14:48

What\'s going on here? What is the subtle difference between the two forms of \"unless\"?

> irb(main):001:0> foo = true unless defined?(foo)
=> nil         


        
相关标签:
7条回答
  • 2020-12-03 14:53

    Apparently, ruby creates local variable at parse time setting them to nilso it is defined and this is done whether the code is executed or not.

    When the code is evaluated at your first line, it doesn't execute the assignment part since foo is set to nil. In the second line, because fooo has not been parsed yet, defined?returns nil letting the code inside the block execute and assign fooo.

    As an example you can try this:

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

    This is taken from a forum post at ruby-forum.

    0 讨论(0)
  • 2020-12-03 15:04
    irb(main)> foo = true unless defined?(Integer)
    => nil 
    irb(main)> foo = true unless defined?(thisIsUndefined)
    => true
    

    Your first block is returning nil because the way it's written leaves 2 options:

    • foo is not defined --> assign true
    • foo is defined --> do nothing

    Here, foo must be defined when the line is evaluated. Thus, nothing happens and nil is returned.

    irb(main)> unless defined?(Integer) ; fooo = false ; end
    => nil
    irb(main)> unless defined?(thisIsUndefined) ; fooo = false ; end
    => false 
    

    Your second block operates the same way your first one does. If fooo is not defined, the block is entered and fooo is set to false. The result of the last line of the block is the return value of the block, thus the false you are seeing. If fooo does exist, then the block is skipped over and nothing happens, therefore there is nothing to return, therefore the nil.

    Based on your code, I would say that foo was defined when this code was run and fooo was not (test code shown was generated in Ruby 1.8.6). If you did not define either of these before running this code, then you may have something called foo that is defined by default (do defined?(foo) by itself to check). Try using a different name and see if you get the same results.

    Edit:

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

    Apparently, defined?() is returning true since it has already seen bar (at the beginning of the line), even though you are still in the process of defining it.

    0 讨论(0)
  • 2020-12-03 15:04

    in the first instance you call foo into existence in the assignment statement. Maybe this will clarify:

    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 讨论(0)
  • 2020-12-03 15:04

    In Ruby 1.8.7:

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

    I don't have an explanation for the behaviour you are seeing.

    0 讨论(0)
  • 2020-12-03 15:06

    Let's start with something simpler:

    # 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
    

    Now we can figure out your first example.

    foo = true unless defined?(foo)
    

    Is foo defined? Before we press ENTER in irb, no. However, the presence of the assignment statement causes foo to come to life. That means the assignment statement won't be executed, leaving foo in existence but having nil as its value. And what is the last expression evaluated in the irb line? It is unless defined?(foo), which evaluates to nil.

    For more info on how assignments (even those that do not get executed) cause variables to exist, see this discussion of Variable/Method Ambiguity.

    In your second example, there is nothing mysterious at all: fooo is not defined, so the code in the block executes, setting fooo to false. That assignment is the last expression evaluated, so false is the return value of our block.

    0 讨论(0)
  • 2020-12-03 15:09

    August, all look fine in 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"
    
    0 讨论(0)
提交回复
热议问题