Simple Ruby puzzle on variable reference in a method - ruby

Why does this method return 1 rather than dying from infinite recursion?
def foo
foo ||= 1
end
foo # => 1
Rewritten the following way it does die:
def foo
foo.nil? ? 1 : foo
end

In the first case, foo ||= 1 refers to a local variable. Ruby always creates a local variable when you do assignment on a bareword, which is why you have to write self.foo = ... if you want to invoke a writer method defined as def foo=(value). The ||= operator is, after all, just a fancy assignment operator.
In the second case, there is no assignment, so when it hits foo.nil?, Ruby interprets the bareword foo as a method call, and blows up.

Related

Conditional assignment from a function with multiple return values in Ruby?

Is there a way to use the conditional assignment operator (||=) to assign to a particular value from a function that returns multiple values?
For example, I've seen this pattern a lot:
def foo
'hello'
end
def bar
#bar ||= foo
end
This works great if foo returns a single value. What about if foo returns two values, and I only want to assign bar to the first value?
def foo
return 'hello', 'world'
end
def bar
#bar ||= foo
end
# How to set bar = 'hello' ?
Is there some way to conditionally assign to only the first value returned? If not -- what would be the idiomatic way to set the bar instance variable to 'hello'?
(EDITED: fixed typo in foo in second example -- forgot to explicitly return.)
Ruby doesn't support multiple return values. It's simply syntax sugar for returning a list. Hence, the following works.
#bar ||= foo[0]

Confused about the assignment method in Ruby (defined method with equal sign) [duplicate]

This question already has answers here:
Why do Ruby setters need "self." qualification within the class?
(3 answers)
Closed 6 years ago.
I am really confused about the assignment method in Ruby. In the documentation, it says:
Methods that end with an equals sign indicate an assignment method.
For assignment methods, the return value is ignored and the arguments
are returned instead.
I know this can be use as setter method in Class. However, I still feel confused about the concept. In the code below:
class Foo
# setter method
def foo=(x)
puts "OK: x=#{x}"
end
end
f = Foo.new
f.foo = 123 # OK: x=123
def a=(value)
puts "value is #{value}"
end
a = 123 # why there is no output?
p a # 123, why this only return the argument?
p a = 123 # 123, why this only return the argument?
Why the method with equal sign run differently in the Class and outside the class?
a = 123
In principle, there is ambiguity here. Is it a setter invocation or a local variable assignment? Ruby resolves this by choosing local variable assignment. If you want the method, be explicit.
self.a = 123
In the other case, there's no ambiguity.
Seeing something like a = 123, the interpreter assumes that this is always an assignment operation. However, you can call self.a = 123- as this cannot be a proper assignment (variable name cannot include a dot); it will invoke a method you defined.
Note that the same happens inside the class, so it is not a different behaviour:
class Foo
def foo=(x)
puts "OK: x=#{x}"
end
def bar
foo = 1
end
def baz
self.foo = 1
end
end
Calling bar will not print your message, it will just perform an assignment. baz will execute your setter method.

Why isn't my variable getting defined, and ending this loop?

I ran across this issue. I have a work around, so I'm specifically asking WHY this doesn't work? Why doesn't the variable get defined in the first time through the loop, and then exit the loop?
Assuming it's the scope, which I'm convinced it is, then why is foo defined after breaking the loop? Am I just seeing an artifact of irb here?
voin0017:[/home/acowell/src/local/goldrhel] ruby -v
ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-linux]
voin0017:[/home/acowell/src/local/goldrhel] irb
irb(main):001:0> while defined?(foo).nil? ; foo = 1 ; end
^CIRB::Abort: abort then interrupt!
from (irb):1:in `call'
from (irb):1
from /opt/chef/embedded/bin/irb:12:in `<main>'
irb(main):002:0> p foo
1
=> 1
irb(main):003:0>
The defined? method does not test if a value is defined for a variable, but instead if that variable is defined within the scope you're testing it in.
The code you've written here is basically nonsensical:
while (defined?(foo).nil?)
foo = 1
end
The foo variable is never "defined" outside of the context of the while block so it spins forever waiting for that to happen. You end up defining it repeatedly inside the context of the while loop, but that defined? check does not test that. It is getting defined, just not where you expected it to be.
If you add a little code to see what's going on you get this:
puts defined?(foo).inspect
# => nil
while (true)
foo = 1
puts defined?(foo).inspect
# => "local-variable"
break
end
# Once the while completes, the variable is defined outside that scope.
puts defined?(foo).inspect
# => "local-variable"
The general pattern you're going in idiomatic Ruby code looks more like this
foo = nil
while (!foo)
foo = 1
end
As a note, it's highly unusual to see defined? used in Ruby applications as variables are either used, or not used. It's in situations where you have some automatic code generation and block rebinding going on that you want to test local variables before using them. The most common case for this rare behaviour is within Rails partials where you may have passed in options via the :local argument. These appear as local variables if defined, but may not exist, so you need to test to be sure.
Whether or not a variable is defined depends upon not only the scope, but also on its location within the ruby script. That is, a variable is defined only if it has been defined previously within the parse, not the execution.
Here's an example:
begin
puts "Is foo defined? #{defined?(foo).inspect}" # foo is never defined here
foo ||= 1
puts "but now foo is #{foo}" # foo is always defined here
foo += 1
end while foo <= 3
Output:
Is foo defined? nil
but now foo is 1
Is foo defined? nil
but now foo is 2
Is foo defined? nil
but now foo is 3
Because foo has not been defined previously within the script on the first line of the loop, it is undefined at that point, and remains undefined, even if it is assigned to and the same line is returned to at a later point during execution.
This is why foo in the while condition of the question is always undefined:
while defined?(foo).nil? # foo is always undefined
foo = 1
end
and will loop forever. In contrast, this loop only executes once:
begin
foo = 1
end while defined?(foo).nil? # foo is defined
because foo is assigned to previously in the parse.
Edit:
Only loops that require a block seem to isolate its local variables from living outside of it. E.g. loop, upto, each, inject, map, times, etc. These all require use of the keyword do and end, or curly braces, which delimit the block. In contrast, while, until, and for do not, and so variables defined within them continue to live outside of them. This is demonstrated here:
while true
foo_while = 1
break
end
puts "foo_while: #{defined?(foo_while).inspect}"
until false
foo_until = 1
break
end
puts "foo_until: #{defined?(foo_until).inspect}"
for i in 0..2
foo_for = 1
break
end
puts "foo_for: #{defined?(foo_for).inspect}"
loop do
foo_loop = 1
break
end
puts "foo_loop: #{defined?(foo_loop).inspect}"
1.upto(2) do |i|
foo_upto = 1
break
end
puts "foo_upto: #{defined?(foo_upto).inspect}"
[1,2,3].each do |i|
foo_each = 1
break
end
puts "foo_each: #{defined?(foo_each).inspect}"
[1,2,3].inject do |i,j|
foo_inject = 1
break
end
puts "foo_inject: #{defined?(foo_inject).inspect}"
[1,2,3].map do |i|
foo_map = 1
break
end
puts "foo_map: #{defined?(foo_map).inspect}"
3.times do
foo_times = 1
break
end
puts "foo_times: #{defined?(foo_times).inspect}"
Output:
foo_while: "local-variable"
foo_until: "local-variable"
foo_for: "local-variable"
foo_loop: nil
foo_upto: nil
foo_each: nil
foo_inject: nil
foo_map: nil
foo_times: nil

Confusion with method call from IRB in Ruby

I was playing with method definition and calling to them in the main of IRB.
def show
p "hi"
end
#=> nil
show
#"hi"
#=> "hi"
self.show
#"hi"
#=> "hi"
The above are good and understood.
Now let's try something different:
def Foo
p "hi"
end
#=> nil
Foo
#NameError: uninitialized constant Foo
#from (irb):4
#from C:/Ruby193/bin/irb:12:in `<main>'
While the call to Foo has thrown an error as above,how does the below remove that?
self.Foo
#"hi"
#=> "hi"
In Ruby, you can call methods without a receiver and without an argument list. However, this means that there is an ambiguity: does foo mean "call method foo on the implicit receiver self without arguments, i.e. equivalent to self.foo()" or does it mean "dereference the variable foo"? Ruby can't know which you mean, so there are some simple rules.
For a local variable, the rule is that foo is always a method call, unless foo is statically known at parse time to be a local variable. So, when is it statically known to be a variable? When there was an assignment to that variable which was parsed (but not necessarily executed!) before the use.
Example:
foo # method call
if false
foo = 42 # will never be executed, but *will* be parsed
end
foo # variable dereference, since the `foo` assignment was parsed
For constant variables, the rule is even simpler: Foo is always interpreted as a constant dereference. Period.
So, how do you call a method with such a name? Easy: like I said, the ambiguity arises only for method calls with no argument list and no explicit receiver. So, if we add either one or both of those, Ruby will know that we are trying to call a method and not dereference a variable:
foo()
self.foo
self.foo()
Foo()
self.Foo
self.Foo()
Of course, in the example you gave above, only the first one will work. When you define a method at the top-level, it is added as a private method to Object, and private methods can only be called without an explicit receiver, even if that receiver is self. So, self.Foo won't work, because Foo is private. (Except in IRb, where, for convenience reasons, top-level methods are public.)

Why foo is not nil anymore - or function within function

Why in below code snippet foo replaces its definition?
def foo
def foo
1
end
end
for the first time foo is nil
foo
=> nil
foo.foo
=> 1
Now if I call foo again:
foo
=> 1
As you can see foo is not nil anymore. Can some one explain this to me? thanks.
def foo
p "about to redef foo"
def foo
1
end
end
foo
"about to redef foo"
=> nil
foo
=> 1
Also, when you call foo.foo, it seems like you’re trying to access the inner foo method, but it doesn’t work that way. Your foo method is actually defined on Object, so you’re actually calling 1.foo.
If you want this effect, try
def foo
foo = proc {
1
}
end
Since def methods do not create a new self. Every method is bound
to self, which is main in this case, an Object.new which is
instantiated for every file loaded by the ruby interpreter. Inside a
class, self is the class, and you get instance methods.
Method definitions are parsed when read, but are not executed until called. When you do the first foo, the outermost foo is executed, which defines Object#foo as
def foo
1
end
and returns nil as the return value of an operation that defined the method. From then on, when you call foo, the newly defined foo is executed, returning
1
When your first call foo it returns method foo and when you call again foo it returns 1.
Read closure
Nested method definitions in ruby are confusing.
They are actually redefinitions!
What happens is that both definitions apply to the outmost context. That is both definitions define the same(!) method foo. Though the outer definition is interpreted when the file is read, while the inner definition is only interpreted when the outer method is called for the first time. It will then replace the initial definition.
Given this code
def foo
def foo
1
end
end
Let's walk through this:
when loading your file a global method foo is defined with the body def foo; 1; end.
when you call foo() this global method is executed and redefines the global method foo with the body 1, and returns nil since defining a method has no return value.
when you call foo().foo() the global method is executed and returns 1, on which the global method is executed again, returning 1 again.
Confusing here are two things, a) that nested method definition both apply to the same outer scope and b) that a global method can be called on any object.
Here's another example to demonstrate how nested definitions are actually redefinitions.
class A
def m(arg=nil)
def m; 42; end if arg
23
end
end
here's what happens
a = A.new
a.m # => 23
a.m # => 23
a.m(:magic) # => 23
a.m # => 42
a.m # => 42
as you can see, the nested definition is actually a redefinition.
Personally I always thought it very strange that it defines inner def on the class. I would think it more sensible to define it on the singleton. e.g. equivalent to def self.foo, since it is being called at the instance level and not the class level.
Either that, or it could only be callable from the method it is defined in --though that might not be as useful since we have lambdas.
One thing is for certain though, you will almost never see this in practice.

Resources