Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
258 views
in Technique[技术] by (71.8m points)

ruby - Confusion with the assignment operation inside a falsy `if` block


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In Ruby, local variables are defined by the parser when it first encounters an assignment, and are then in scope from that point on.

Here's a little demonstration:

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

if false
  foo = 42
end

foo # => nil

As you can see, the local variable does exist on line 7 even though the assignment on line 4 was never executed. It was, however, parsed and that's why the local variable foo exists. But because the assignment was never executed, the variable is uninitialized and thus evaluates to nil and not 42.

In Ruby, most uninitialized or even non-existing variables evaluate to nil. This is true for local variables, instance variables and global variables:

defined? foo       #=> nil
local_variables    #=> []
if false
  foo = 42
end
defined? foo       #=> 'local-variable'
local_variables    #=> [:foo]
foo                #=> nil
foo.nil?           #=> true

defined? @bar      #=> nil
instance_variables #=> []
@bar               #=> nil
@bar.nil?          #=> true
# warning: instance variable @bar not initialized

defined? $baz      #=> nil
$baz               #=> nil
# warning: global variable `$baz' not initialized
$baz.nil?          #=> true
# warning: global variable `$baz' not initialized

It is, however, not true for class hierarchy variables and constants:

defined? @@wah     #=> nil
@@wah
# NameError: uninitialized class variable @@wah in Object

defined? QUUX      #=> nil
QUUX
# NameError: uninitialized constant Object::QUUX

This is a red herring:

defined? fnord     #=> nil
local_variables    #=> []
fnord
# NameError: undefined local variable or method `fnord' for main:Object

The reason why you get an error here is not that unitialized local variables don't evaluate to nil, it is that fnord is ambiguous: it could be either an argument-less message send to the default receiver (i.e. equivalent to self.fnord()) or an access to the local variable fnord.

In order to disambiguate that, you need to add a receiver or an argument list (even if empty) to tell Ruby that it is a message send:

self.fnord
# NoMethodError: undefined method `fnord' for main:Object
fnord()
# NoMethodError: undefined method `fnord' for main:Object

or make sure that the parser (not the evaluator) parses (not executes) an assignment before the usage, to tell Ruby that it is a local variable:

if false
  fnord = 42
end
fnord              #=> nil

And, of course, nil is an object (it is the only instance of class NilClass) and thus has an object_id method.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...