Reprinted from Blog Article.
Recently, I wrote a few people who are addicted to it, so I will summarize it for a moment.
In Ruby, when calling a method, you can "call the method by omitting()
" compared to other languages.
def hoge(a = nil)
"#hoge(#{a})"
end
#You can call the method without the parentheses
p hoge
# => "#hoge()"
#You can refer to the method even with a method-like call
p hoge() # => "#hoge()"
p hoge 42 # => "#hoge(42)"
p self.hoge # => "#hoge()"
Well this is right.
The problem is when "methods and variables with the same name" are mixed. In this case, refer to "Variable if the variable can be referenced" and "Otherwise" method.
def hoge(a = nil)
"#hoge(#{a})"
end
#Since the variable is not defined at this point, the method is called with priority.
p hoge # => #hoge()
#Define variables
hoge = 42
#After defining the variable, give priority to the variable.
p hoge # => 42
#In a method-like call, call the method
p hoge() # => "#hoge()"
p hoge 42 # => "#hoge(42)"
p self.hoge # => "#hoge()"
At this time, refer to "Method if it is before the assignment expression" and "Variable if it is after the assignment expression".
For example, in Ruby, there is a "case where the process that actually defines the variable is not called" as follows.
def hoge(a = nil)
"#hoge(#{a})"
end
if false
hoge = 42
end
#Is this a method reference?
p hoge
In the interpreter language, this may be expected to be "the method is called because the variable is not defined".
However, in reality, p hoge
refers to the" variable hoge
'.
This is a secret to how Ruby executes the source code.
In Ruby, before executing the source code, first "parse all the Ruby source code" and then execute the Ruby code.
So, like the code above, the variable hoge
is implicit when the" assignment expression "is defined because all the source code is parsed regardless of" whether the code is called at runtime ". It will be defined in.
Therefore, regardless of whether the contents of the if statement are actually called, when the "assignment expression" is defined, the "variable hoge
"will be referenced even" outside the if statement ".
#Actual execution result
def hoge(a = nil)
"#hoge(#{a})"
end
if false
hoge = 42
end
#Reference variables here
#Returns nil because the default value of the variable is nil
p hoge
# nil
It's very difficult to understand how Ruby parses source code.
For example, what happens if you "define a hoge
variable while referring to hoge
with a postfix if" as follows?
hoge = 42 if hoge.nil?
p hoge
Many people think that this is because ʻif hoge.nil?Is processed before the variable definition, so the process
hoge = 42is not actually called. However, in reality, the code
hoge = 42 if hoge.nil? Is parsed as "this is one process in total". Therefore, when ʻif hoge.nil?
Is called, the code hoge = 42
has already been parsed, and the process is that the variable hoge
is defined.
So, the result of executing the code above is
hoge = 42 if hoge.nil?
p hoge
# => 42
Then, " hoge = 42
"is processed.
On the other hand, if it is not a postfix if, the result will be different, so you need to be careful.
#This will result in an error
# error: undefined local variable or method `hoge' for main:Object (NameError)
if hoge.nil?
hoge = 42
end
This is because ʻif hoge.nil?` Is parsed before the variable and the conditional expression of the if statement is executed "in the state where the variable is not defined".
In Ruby, there is a method called ʻeval`. This is a method that executes the Ruby source code "at runtime".
def hoge(a = nil)
"#hoge(#{a})"
end
hoge = 42
#Execute the string passed to eval as Ruby code
p eval("hoge + hoge")
# => 84
When the above code is executed, `" hoge + hoge "is executed after the assignment expression, so refer to" variable ". So what happens when you run code like this:
def hoge(a = nil)
"#hoge(#{a})"
end
#Run hoge before the assignment expression
p eval("hoge")
hoge = 42
From the flow explained earlier, "I am executing"hoge"
before the variable", so I expect this to be a "method call".
However, when it is actually executed, the result is as follows.
def hoge(a = nil)
"#hoge(#{a})"
end
#Refer to a variable instead of a method call
p eval("hoge")
# => nil
hoge = 42
The reason for this result is that "the timing of Ruby source code" and "the timing of executing ʻeval" are different. The timing to execute ʻeval
is" Ruby runtime ".
This "runtime" is "after the Ruby source code has been parsed".
In other words, since "timing to execute ʻeval" is already "after the Ruby source code has been parsed", the expression
hoge refers to" variable priority ". Therefore, when referencing a variable or method from ʻeval
, the "variable" is preferentially called regardless of the "definition position of the assignment expression".
This also affects when using binding.irb
, which is a problem when calling binding.irb
"before the assignment expression", for example:
def hoge(a = nil)
"#hoge(#{a})"
end
#Binding for debugging etc..Start irb at runtime with irb
#If you refer to hoge on this irb console, you will see "variables".
binding.irb
hoge = 42
binding.irb
executes the entered Ruby code using ʻeval. Therefore, it will be executed by referring to the "variable" as in the previous example. I think it's rare to use ʻeval
in normal Ruby code, but you need to be careful because using binding.irb
etc. indirectly means using ʻeval`. ..
hoge
"is a method call or a variable reference.or
binding.irb`So, I tried to summarize the variables of Ruby.
When you actually call a method with binding.irb
, nil
may be returned, and if you look closely at the code, a variable name with the same name is defined after binding.irb
. There was a thing.
As you can see, there are some addictive points in Ruby, so be careful.
Recommended Posts