Instance Variable in Ruby Resetting as Nil - ruby

My instance variables gets turned back to nil, even though it was set in a separate function that is called.
I've tried printing out before and after values for the instance variable, and have seen where it turns to nil. It's quite puzzling though. Attaching an example (https://repl.it/repls/FirebrickPresentKeyboard) also below:
class Test
def process
return if a.nil? && b.nil?
puts #some
end
def a
#some = nil
return true
end
def b
#some = "abc"
return false
end
end
class Test2
def process
return if c.nil?
puts #hello
end
def c
#hello = "hello"
return true
end
end
t = Test.new
t.process
t2 = Test2.new
t2.process
In the Test class, I expect #some to print "abc" since it is set during the "b" function. However, it prints nil.
In the Test2 class, I expect #hello to print "hello" and it certainly does.

In this example your method b never executes: && returns its first argument if it is falsy. Otherwise, it evaluates and returns its second argument. Since a.nil? evaluates to false the second expression never gets called.
Try this:
def process
return if a && b
puts #some
end

Related

RUBY Exit minesweeper game loop

I can't figure out how to exit the game loop. I'm having a hard tried making a lose? function, I tried doing like lose?(x) that would return true when x==1 but that didn't get it to exit the run method. Here's my code for the Game class.
class Minesweeper
attr_accessor :board
def initialize
#board = Board.new
end
def run
puts "Welcome to minesweeper!"
x = nil
play_turn until win? || lose?(x)
end
def play_turn
board.render
pos, command = get_input
debugger
if !explode?(pos, command)
board.set_input(pos,command)
else
puts "You lose!"
lose?(1)
end
end
def explode?(pos, cmd)
board.grid[pos[0]][pos[1]].bomb && cmd == "reveal"
end
def get_input
pos = nil
command = nil
until pos && check_pos(pos)
puts "What position?"
pos = parse_pos(gets.chomp)
end
until command && check_command(command)
puts
puts "What would you like to do (e.g. reveal, flag... ~ else?)"
command = gets.chomp
puts
end
[pos, command]
end
#Some code here (check_pos, etc)
def lose?(x)
return true if x == 1
false
end
def win?
# board.all? {}
end
end
I had explode? in the Board class before, but for the sake of being able to end the game, moved it here. Any help is greatly appreciated!
If we expand your run method, a little, it becomes more obvious what the issue is:
def run
puts "Welcome to minesweeper!"
x = nil
until(win? || lose(x)) do
play_turn
end
end
In this method, you're creating a new variable x which is scoped to this method and has a value of nil. This variable is never set in the scope of that method, so is always nil, meaning that the check win? || lose(x) could also be written win? || lose(nil), and lose will never return true.
If you return a value from your play_turn method, you can then use that. Note that the result of the last thing executed in the method is what is returned:
def play_turn
board.render
pos, command = get_input
debugger
if !explode?(pos, command)
board.set_input(pos,command)
true
else
puts "You lose!"
false
end
end
which means your run method can then check the result:
def run
puts "Welcome to minesweeper!"
# we now know that play_turn returns true if the turn was not a loser
# (i.e. it should continue to the next loop) and false if the player
# lost, so we can use that returned value in our loop.
while(play_turn) do
if win?
puts "looks like you won!"
# if the player wins, we want to exit the loop as well. This is done
# using break
break
end
end
end
Note that this also means that you don't need a separate lose method

Understanding returning from procs in Ruby

I was wondering how to pass a block to a method which will make the method return on yield.
The naive aproach doesn't work:
def run(&block)
block.call
end
run { return :foo } # => LocalJumpError
Wrapping in another proc has the same effect:
def run(&block)
proc { block.call }.call
end
run { return :bar } # => LocalJumpError
So I thought that the return statement is bound to the receiver of the current binding. However, trying it out with instance_eval proved me wrong:
class ProcTest
def run(&block)
puts "run: #{[binding.local_variables, binding.receiver]}"
instance_eval(&block)
end
end
pt = ProcTest.new
binding_inspector = proc { puts "proc: #{[binding.local_variables, binding.receiver]}" }
puts "main: #{[binding.local_variables, binding.receiver]}"
# => main: [[:pt, :binding_inspector], main]
binding_inspector.call
# => proc: [[:pt, :binding_inspector], main]
pt.run(&binding_inspector)
# => run: [[:block], #<ProcTest:0x007f4987b06508>]
# => proc: [[:pt, :binding_inspector], #<ProcTest:0x007f4987b06508>]
pt.run { return :baz }
# => run: [[:block], #<ProcTest:0x007f4987b06508>]
# => LocalJumpError
So the questions are:
How can this be done?
How is the return context tied to the return statement. Is this connection accessible via the language's API?
Was this implemented in such manner intentionally? If yes - why? If no - what are the obstacles to fix it?
I thought that the return statement is bound to the receiver of the current binding.
Only methods have an receiver. return is not a method:
defined? return #=> "expression"
Trying to invoke it as a method doesn't work:
def foo
send(:return, 123)
end
foo #=> undefined method `return'
trying it out with instance_eval proved me wrong
Though instance_eval evaluates the block in the context of the receiver (so you have access to the receivers instance methods and instance variables):
class MyClass
def foo(&block)
#var = 123
instance_eval(&block)
end
end
MyClass.new.foo { instance_variables }
#=> [:#var]
... it does not evaluate the block in the current binding (so you don't have access to any local variables):
class MyClass
def foo(&block)
var = 123
instance_eval(&block)
end
end
MyClass.new.foo { local_variables }
#=> []
How can this be done?
You could use eval, but that requires a string:
def foo
var = 123
eval yield
nil
end
foo { "return var * 2" }
#=> 246
Or by passing the binding to the block (again using eval):
def foo
var = 123
yield binding
nil
end
foo { |b| b.eval "return var * 2" }
#=> 246
return in a block returns from the enclosing method when the block is defined (ie, the closure in which the block is created). In your example, there is no enclosing block to return from, hence your exception.
This is easily demonstrated:
def foo(&block)
puts yield
puts "we won't get here"
end
def bar
foo { return "hi from the block"; puts "we never get here" }
puts "we never get here either"
end
puts bar # => "hi from the block" (only printed once; the puts in `foo` is not executed)
Return in a proc will immediately return out of the proc, not out of the method on the stack under the proc:
def foo(&block)
puts yield
puts "we will get here"
end
def bar
foo &->{ return "hi from the proc"; puts "we never get here" }
puts "we will get here too"
end
puts bar
# hi from the proc # puts from foo
# we will get here # puts from foo
# we will get here too # puts from bar
Because of these behaviors, there is no way to achieve your desired behavior, in which a return in the given block will execute a return in the method from which the block is invoked, unless the block was defined within that scope, since doing so would require one of the existing behaviors not work.
You could achieve something like this with throw...catch, which is kinda-sorta useful as a way to zip up the stack from an arbitrary depth, but you can't return arbitrary values with it:
def foo(&block)
yield
puts "we won't get here"
end
catch(:escape) do
foo &->{ throw :escape }
end

Calling super without arguments

According to the documentation for modules and classes, calling super (without arguments or parentheses) calls the parent method with the same arguments:
When used without any arguments super uses the arguments given to the subclass method.
Assigning a new value to the "argument variable" seems to alter this behavior:
class MyClass
def foo(arg)
puts "MyClass#foo(#{arg.inspect})"
end
end
class MySubclass < MyClass
def foo(arg)
puts "MySubclass#foo(#{arg.inspect})"
super
arg = 'new value'
super
end
end
MySubclass.new.foo('inital value')
Output:
MySubclass#foo("inital value")
MyClass#foo("inital value")
MyClass#foo("new value") # <- not the argument given to MySubclass#foo
Is this expected?
Update
This seems to be the expected behavior for positional and keyword arguments, but it doesn't work for block arguments:
class MyClass
def foo(&block)
puts "MyClass#foo { #{block.call.inspect} }"
end
end
class MySubclass < MyClass
def foo(&block)
puts "MySubclass#foo { #{block.call.inspect} }"
super
block = Proc.new { 'new value' }
super
end
end
MySubclass.new.foo { 'initial value' }
Output:
MySubclass#foo { "initial value" }
MyClass#foo { "initial value" }
MyClass#foo { "initial value" }
Lets take one example from the Ruby core:
Keyword2
class Base
def single(a) a end
def double(a, b) [a,b] end
def array(*a) a end
def optional(a = 0) a end
def keyword(**a) a end
end
class Keyword2 < Base
def keyword(foo: "keyword2")
foo = "changed1"
x = super
foo = "changed2"
y = super
[x, y]
end
end
Now, see the test case :-
def test_keyword2
assert_equal([{foo: "changed1"}, {foo: "changed2"}], Keyword2.new.keyword)
end
Above example exactly mathes the keyword documentation.
Called with no arguments and no empty argument list, super calls the appropriate method with the same arguments, and the same code block, as those used to call the current method. Called with an argument list or arguments, it calls the appropriate methods with exactly the specified arguments (including none, in the case of an empty argument list indicated by empty parentheses).
same arguments means it is saying the current values of argument variables.test_super.rb files contains all the varieties of stuffs we can do with super in Ruby.
No, it work with block too (taken from core) :
a = Class.new do
def foo
yield
end
end
b = Class.new(a) do
def foo
super{
"b"
}
end
end
b.new.foo{"c"} # => "b"
But, have no idea why the below is giving "c"? This is actually the updated question of the OP:
c = Class.new do
def foo(&block)
block.call
end
end
d = Class.new(c) do
def foo(&block)
block = -> { "b" }
super
end
end
d.new.foo{"c"} # => "c"
It seems to be the expected behavior, based on the RubySpec anyway.
module RestArgsWithSuper
class A
def a(*args)
args
end
end
class B < A
def a(*args)
args << "foo"
super
end
end
end
(language/fixtures/super.rb).
It's then expected that the arguments are modified:
it "passes along modified rest args when they weren't originally empty" do
Super::RestArgsWithSuper::B.new.a("bar").should == ["bar", "foo"]
end
(language/super_spec.rb)
It's the expected behaviour. Technically, arg is the same argument, it just points to another value.
This answer might explain it better: https://stackoverflow.com/a/1872159/163640

Passing a method that--when read--is the equivalent of return true

I have a method that accepts a method as an argument:
def acceptor_method(received_method)
an_arry.each do |attr|
received_method if some_condition
end
end
This works well if all the received_method does is run through some code:
def some_method
do_something
end
Which is the equivalent of:
def acceptor_method(received_method)
an_arry.each do |attr|
do_something if some_condition
end
end
But what if I want the received_method to break the loop and return a value, as with:
def acceptor_method(received_method)
an_arry.each do |attr|
return true if some_condition
end
end
Unfortunately, this doesn't work:
def some_method
return true
end
As it only returns true for some method, not for acceptor_method--which continues to play through the loop.
So is there a way to send a method that when run is the equivalent of return true?
def acceptor_method
[1, 2, 3, 4].each do |attr|
ret = yield attr
puts attr
end
end
test = acceptor_method do |attr|
break 'test' if attr == 3
end
puts test
outputs:
1
2
test
You can do this using blocks rather than methods. See How can I return something early from a block?
Basically if you have a block with break value and yield to it, the function will return value. Unfortunately I don't see a way to do this using methods, since Ruby really doesn't like having break outside of a block or loop.

How do you override the ruby case equality operator? (===)

I have a class that I want to compare to both strings and symbols in a case statement, so I thought that I just override the ===() method for my class and all would be gold. However my ===() method never gets called during the case statement. Any ideas?
Here is some example code, and what happens in a irb session:
class A
def initialize(x)
#x=x #note this isn't even required for this example
end
def ===(other)
puts "in ==="
return true
end
end
irb(main):010:0> a=A.new("hi")
=> #
irb(main):011:0> case a
irb(main):012:1> when "hi" then 1
irb(main):013:1> else 2
irb(main):014:1> end
=> 2
(it never prints the message and should always return true anyway)
Note that ideally I'd like to do a
def ===(other)
#puts "in ==="
return #x.===(other)
end
Thanks in advance.
The expression after the 'case' keyword is the right hand side of the === expression, and the expression after the 'when' keyword is on the left hand side of the expression. So, the method that is being called is String.===, not A.===.
A quick approach to reversing the comparison:
class Revcomp
def initialize(obj)
#obj = obj
end
def ===(other)
other === #obj
end
def self.rev(obj)
Revcomp.new(obj)
end
end
class Test
def ===(other)
puts "here"
end
end
t = Test.new
case t
when Revcomp.rev("abc")
puts "there"
else
puts "somewhere"
end

Resources