In the code below, break exits the program totally and returns to irb, while I want to get back to the line where the print statement is.
def f1()
do_something
end
def f2()
do_something
end
def f1()
print "I want to get back here."
while true
choice = gets.chomp
if choice == "exit"
break
else
f2()
end
end
end
How do you break just one level up?
Add another loop outside. It may also be simpler to use loop than while true:
def f1()
loop do
print "I want to get back here."
loop do
choice = gets.chomp
if choice == "exit"
break
else
f2()
end
end
end
end
Related
I have the following two classes I fabricated for simplicity. I would like to take the information given in the first class, and use it in other classes throughout the program. However, I can not seem to get the variable to retain it's value given by the user.
class Input
attr_accessor :input
def initialize
#input = input
end
def call
get_input
# Changer.new.change(#input)
output
end
def get_input
puts "please write a number"
#input = gets.chomp.to_s
end
def output
p Changer.new.change(#input)
end
end
class Changer < Input
def change(input)
if #input == "one"
#input = "1"
elsif #input == "two"
#input = "2"
elsif #input == nil
"it's nil"
else
"something else"
end
end
end
Input.new.call
I have tried a few variations on the above classes, some with inheritance, some without, initializing, or not, etc. They all seem to output 'nil'. Please advise. Thank you for your time.
When the change method in Changer runs, #input is an instance variable specific to that Changer instance, and it is nil.
You want your change method to work on the input argument being provided to it, rather than #input.
def change(input)
if input == "one"
"1"
elsif input == "two"
"2"
elsif input == nil
"it's nil"
else
"something else"
end
end
Or better yet:
def change(input)
case input
when "one"
"1"
when "two"
"2"
when nil
"it's nil"
else
"something else"
end
end
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
I am trying to use 'gets' to get input and then check to see if the instance variable includes the input inside of a method but I can't get it work. Here is the code:
class Game
attr_accessor :available_moves
def initialize
#available_moves = ["0","1","2","3","4","5","6","7","8"]
end
def play_game
puts ("Welcome")
while true
puts "Player 1, choose a square"
player1_choice = gets
# v this is what I can't get to work v
if #available_moves.include?(player1_choice)
puts "yes"
break
else
puts "no"
end
end
end
end
game1 = Game.new
game1.play_game
No matter what I try, the 'else' condition is met and "no" is printed.
When the user inputs text with gets they press Enter, which sends a newline. You need to remove the newline with gets.chomp:
class Game
attr_accessor :available_moves
def initialize
#available_moves = ["0","1","2","3","4","5","6","7","8"]
end
def play_game
puts ("Welcome")
while true
puts "Player 1, choose a square"
# Note the .chomp here to remove the newline that the user inputs
player1_choice = gets.chomp
# v this is what I can't get to work v
if #available_moves.include?(player1_choice)
puts "yes"
break
else
puts "no"
end
end
end
end
game1 = Game.new
game1.play_game
Now you get:
game1.play_game
Welcome
Player 1, choose a square
1
yes
=> nil
See How does gets and gets.chomp in ruby work? for a more in-depth explanation.
Build the now_serving method which should call out (i.e. puts) the next person in line and then remove them from the front. If there is nobody in line, it should call out (puts) that "There is nobody waiting to be served!". When I try to use the shift method to get rid of the first element in the array. I end up with the wrong output. Here is the Ruby code:
def now_serving(array)
while array.length != 0
array.each do |name|
puts "Currently serving #{name}."
array.shift
end
end
puts "There is nobody waiting to be served!"
end
But the array.shift is only working once, How do I get it to drop the first element of an array continuously. Here is the code for the Rspec:
describe "#now_serving" do
context "there are no people in line" do
it "should say that the line is empty" do
expect($stdout).to receive(:puts).with("There is nobody waiting to be served!")
now_serving(katz_deli)
end
end
context "there are people in line" do
it "should serve the first person in line and remove them from the queue" do
expect($stdout).to receive(:puts).with("Currently serving Logan.")
now_serving(other_deli)
expect(other_deli).to eq(%w(Avi Spencer))
end
end
end
end
It is a bad idea to shift an array at the same time you loop through it.
def now_serving(array)
while name = array.shift
puts "Currently serving #{name}."
end
puts "There is nobody waiting to be served!"
end
So this fixed my bug
def now_serving(array)
queue = Queue.new
queue = array
if array.length > 0
puts "Currently serving #{array[0]}."
array.shift
else
puts "There is nobody waiting to be served!"
end
end
Here is Bar#do_things:
class Bar
def do_things
Foo.some_method(x) do |x|
y = x.do_something
return y_is_bad if y.bad? # how do i tell it to stop and return do_things?
y.do_something_else
end
keep_doing_more_things
end
end
And here is Foo#some_method:
class Foo
def self.some_method(targets, &block)
targets.each do |target|
begin
r = yield(target)
rescue
failed << target
end
end
end
end
I thought about using raise, but I am trying to make it generic, so I don't want to put anything any specific in Foo.
Use the keyword next. If you do not want to continue to the next item, use break.
When next is used within a block, it causes the block to exit immediately, returning control to the iterator method, which may then begin a new iteration by invoking the block again:
f.each do |line| # Iterate over the lines in file f
next if line[0,1] == "#" # If this line is a comment, go to the next
puts eval(line)
end
When used in a block, break transfers control out of the block, out of the iterator that invoked the block, and to the first expression following the invocation of the iterator:
f.each do |line| # Iterate over the lines in file f
break if line == "quit\n" # If this break statement is executed...
puts eval(line)
end
puts "Good bye" # ...then control is transferred here
And finally, the usage of return in a block:
return always causes the enclosing method to return, regardless of how deeply nested within blocks it is (except in the case of lambdas):
def find(array, target)
array.each_with_index do |element,index|
return index if (element == target) # return from find
end
nil # If we didn't find the element, return nil
end
I wanted to just be able to break out of a block - sort of like a forward goto, not really related to a loop. In fact, I want to break of of a block that is in a loop without terminating the loop. To do that, I made the block a one-iteration loop:
for b in 1..2 do
puts b
begin
puts 'want this to run'
break
puts 'but not this'
end while false
puts 'also want this to run'
end
Hope this helps the next googler that lands here based on the subject line.
If you want your block to return a useful value (e.g. when using #map, #inject, etc.), next and break also accept an argument.
Consider the following:
def contrived_example(numbers)
numbers.inject(0) do |count, x|
if x % 3 == 0
count + 2
elsif x.odd?
count + 1
else
count
end
end
end
The equivalent using next:
def contrived_example(numbers)
numbers.inject(0) do |count, x|
next count if x.even?
next (count + 2) if x % 3 == 0
count + 1
end
end
Of course, you could always extract the logic needed into a method and call that from inside your block:
def contrived_example(numbers)
numbers.inject(0) { |count, x| count + extracted_logic(x) }
end
def extracted_logic(x)
return 0 if x.even?
return 2 if x % 3 == 0
1
end
use the keyword break instead of return
Perhaps you can use the built-in methods for finding particular items in an Array, instead of each-ing targets and doing everything by hand. A few examples:
class Array
def first_frog
detect {|i| i =~ /frog/ }
end
def last_frog
select {|i| i =~ /frog/ }.last
end
end
p ["dog", "cat", "godzilla", "dogfrog", "woot", "catfrog"].first_frog
# => "dogfrog"
p ["hats", "coats"].first_frog
# => nil
p ["houses", "frogcars", "bottles", "superfrogs"].last_frog
# => "superfrogs"
One example would be doing something like this:
class Bar
def do_things
Foo.some_method(x) do |i|
# only valid `targets` here, yay.
end
end
end
class Foo
def self.failed
#failed ||= []
end
def self.some_method(targets, &block)
targets.reject {|t| t.do_something.bad? }.each(&block)
end
end
next and break seem to do the correct thing in this simplified example!
class Bar
def self.do_things
Foo.some_method(1..10) do |x|
next if x == 2
break if x == 9
print "#{x} "
end
end
end
class Foo
def self.some_method(targets, &block)
targets.each do |target|
begin
r = yield(target)
rescue => x
puts "rescue #{x}"
end
end
end
end
Bar.do_things
output: 1 3 4 5 6 7 8
You have four ways to unwind the stack in 'non-exceptional' ways: next, break, return, and throw.
next will cause the block to return.
break will cause the method that yielded to the block to return.
return will cause the method where the block is defined to return.
throw will traverse up the stack until it finds a catch with a matching symbol, and cause that to return. This is much like a 'lightweight' exception for non-exceptional situations.
All of them can take a return value that will be returned by whatever they caused to return instead of the value their last expression which they would return normally.
Here are some examples:
def doSomething
puts "> doSomething"
yield
puts "< doSomething"
end
def withNext
puts "> withNext"
doSomething do
puts "> block"
puts "* NEXT! causes the block to return to doSomething"
next
puts "< block"
end
puts "< withNext"
end
def withBreak
puts "> withBreak"
doSomething do
puts "> block"
puts "* BREAK! causes doSomething to return to withBreak"
break
puts "< block"
end
puts "< withBreak"
end
def withReturn
puts "> withReturn"
doSomething do
puts "> block"
puts "* RETURN! causes withReturn to return"
return
puts "< block"
end
puts "< withReturn"
end
def withThrow
puts "> withThrow"
catch :label do
puts "> catch :label"
doSomething do
puts "> block 1"
doSomething do
puts "> block 2"
puts "* THROW! causes catch :label to return to withThrow"
throw :label
puts "< block 2"
end
puts "< block 1"
end
puts "< catch :label"
end
puts "< withThrow"
end
withNext
puts "* Done"
puts
withBreak
puts "* Done"
puts
withReturn
puts "* Done"
puts
withThrow
puts "* Done"
output:
> withNext
> doSomething
> block
* NEXT! causes the block to return to doSomething
< doSomething
< withNext
* Done
> withBreak
> doSomething
> block
* BREAK! causes doSomething to return to withBreak
< withBreak
* Done
> withReturn
> doSomething
> block
* RETURN! causes withReturn to return
* Done
> withThrow
> catch :label
> doSomething
> block 1
> doSomething
> block 2
* THROW! causes catch :label to return to withThrow
< withThrow
* Done
To break out from a ruby block simply use return keyword return if value.nil? next.
next terminates lambda, block, or proc it is in.
break terminates the method that yielded to the block or invoked the proc or lambda it is in.
Credit to: Ruby block return, break, next