In Kernel#loop documentation, there is an example that uses break in order to break out of the loop. Otherwise, the documentation talks about rasing a StopIteration error.
I tried them both:
i=0
loop do
puts i
i+=1
break if i==10
end
i=0
loop do
puts i
i+=1
raise StopIteration if i==10
end
The output is the same. Are there differences between the two approaches? I think that there should be, otherwise why bother to define an error class and all the managements that come with it?
break is a keyword in ruby, which terminates the most internal loop, whether it be loop, or for, and a few more (see here).
StopIteration is an exception, which is caught by Kernel.loop (see here).
So in your scenario, they are the same, but in a different scenario, they will act differsntly:
puts "first run"
for i in 0..20 do
puts i
break if i==10
end
puts "first done"
puts "second run"
for i in 0..20 do
puts i
raise StopIteration if i==10
end
puts "second done" # <= will not be printed
Here is a scenario where StopIteration can be utilized when break cannot:
puts "first run"
def first_run(i) # will not compile
puts i
break if i==10
end
i=0
loop do
first_run(i)
i+=1
end
puts "first done"
puts "second run"
def second_run(i)
puts i
raise StopIteration if i==10
end
i=0
loop do
second_run(i)
i+=1
end
puts "second done"
Here is another use-case, which uses the fact the Enumerator.next throws a StopIteration error when the Enumerator has reached the end:
enum = 5.times.to_enum
loop do
p enum.next
end
will print
0
1
2
3
4
Thanks Cary for this example.
There are two uses of the break keyword.
First: the break keyword, when in a block, causes the method that the block was passed to to return. If you pass an argument to break, the return value of the method will be that argument. Here is an example:
def a
puts 'entering method a'
yield
puts 'leaving method a'
end
result = a { break 50 }
puts result
This will print:
entering method a
50
Second: the break keyword can cause a while, until, or for loop to terminate. Here is an example:
i = 0
result =
while i < 5
i += 1
puts i
break 75 if i == 3
end
puts result
This will print:
1
2
3
75
Your example with Kernel#loop makes use of the first case, causing the loop method to return.
StopIteration is an exception which only works with Kernel#loop as far as I can tell. For example:
infinite_loop = Enumerator.new do |y|
i = 0
while true
y << i
i += 1
end
end
infinite_loop.each do |x|
puts x
raise StopIteration if x == 4
end
Fails because StopIteration is uncaught, however:
x = 0
loop.each do
puts x
x += 1
raise StopIteration if x == 4
end
Catches StopIteration and exits.
Related
I have this bit of code:
gates.each do |key, value, temp|
unless value.class == Output
temp = value.in1
gates.each do |k, v|
if v.out == temp
value.base_distance += 1
#do a thing
end
end
end
end
What I want to happen is when the #do a thing comment inside the conditional is reached it should break out of the inner .each loop and move on to the next instance of the outer .each loop, essentially executing a next. How would I do that from inside the conditional?
TL;DR use break.
Here is the MCVE:
[1,2].each do |o|
puts "Outer: #{o}"
[1,2,3].each do |i|
break if i.even?
puts "Inner: #{i}"
end
end
#⇒ Outer: 1
# Inner: 1
# Outer: 2
# Inner: 1
FWIW, one might pass an argument to break to be returned from the block. This might be needed to emulate the next after break:
[1,2].each do |o|
puts "Outer: #{o}"
inner =
[1,2,3].each do |i|
break :next if i.even?
puts "Inner: #{i}"
end
next if inner == :next
...
end
Ok i seriously suck at passing method to methods whenever i want to return something from the method. Can you guys explain on how do i go about passing it.
Here's my hash
$choosen_gun = {}
$Weapon = {
:Bazoka => ["Bazoka",5],
:Machine_gun => ["Machine_gun",1000],
:Hand_gun => ["Hand_gun",24,2],
:Double_Hand_gun => ["Double_Hand_gun",24,4],
:Sniper => ["Sniper",12,1],
:Shot_gun => ["Shot_gun",8,2]
}
Here's my code for method Weapon
def Weapon
puts "Now it's time to select your weapon."
puts "Please choose a weapon that is good throughout the game."
puts "Whenever you are shortage of bullets, please reload it."
puts "Please avoid last minute of reloading of weapon."
puts "Now choose your weapon based on your own preferences."
print "\n"
puts "Type 1"
puts "Gun Name: Bazoka"
puts "Description: A powerful gun that is strong with only 5 bullets."
puts "Rating: ★ ★ ★ ★"
num = gets.chomp.to_i
case num
when 1
puts "Selection of Bazoka is chosen"
puts "Loaded 5 bullets only"
$choosen_gun[num] = $Weapon[:Bazoka]
end
return num
end
Upon calling the method. The user will choose his weapon and it will add it to the $choosen_gun hash with it's num, and it's return it's num what the user types
Here's my code for method ZombieRoom
def ZombieRoom(w)
zombie = {
:Construcied => [5],
:Invader => [5],
:Damned => [5],
:Steampunk => [5],
:Stoner => [5],
:Wasted => [5],
:Romero => [5]
}
puts "Welcome to the worst night mare of Zombie Room"
puts "You will be fighting with a random zombie"
while true
puts ".........."
puts "Selecting a random zombie"
puts "Selecting your prefered gun...."
case w
when 1
$choosen_gun[1]
puts "Your selected gun is #{$choosen_gun[1][0]}"
#values = zombie.values
#puts values[rand(values.size)]
#random_zombie = zombie.keys.sample(1)
#puts random_zombie[
random_zombie = zombie.to_a.sample(1).to_h
random_zombie.each do |key,value|
puts "Your random zombie is #{key}"
puts "With a health value of #{value[0]}"
puts "Time to take down that zombie now."
while true
puts "Type Shoot to knock it down or quit."
choice = gets.chomp
if $choosen_gun[1][1] >= 1
health = value[0] -= 1
$choosen_gun[1][1] -= 1
puts "#{key} health now is #{health}"
else
puts "Please reload your gun"
puts "Reloading......"
$choosen_gun[1][1] += 5
end
if health == 0
puts "You have defeated #{key}"
puts "Congrats!!!"
puts "We are happy for you"
puts "Lets begins to collect your prize"
CollectPrize()
else
puts "You did not defeat the #{key} yet"
end
end
end
end
end
end
Here's my code for method CollectPrize
def CollectPrize
puts "Congratulations on defeating"
puts "We would now like to give you some case prizes"
print "\n"
puts "Please choose only 1 prize for yourself"
print "\n"
puts "Type 1"
puts "$50,000"
print "\n"
puts "Type 2"
puts "$25,000"
print "\n"
puts "Type 3"
puts "$55,000"
hoho = gets.chomp.to_f
if hoho == 1
puts "hehe"
end
end
Here how i call my method
ZombieRoom(Weapon())
CollectPrize()
Now the problem is that whenever the CollectPrize method is called and i type my input to collect the prize example 1 then it print "$50,000". instead of the ending the problem, it went back to the ZombieRoom and continues to loop at the "Type Shoot to knock it down or quit." Can someone atleast tell me a proper way to solve this issue or also what other way to pass a method?
Your code is in a large while true loop. Since true is always true, it will never end, so after it calls CollectPrize() it just goes back to the while statement.
You could get out of it by inserting a break line after the CollectPrize() but there's another while true loop around this one.
I think you need to pay closer attention to how you want to exit the while loops.
puts "Time to take down that zombie now."
while true # <---------------- this is ALWAYS going to loop, without end
puts "Type Shoot to knock it down or quit."
choice = gets.chomp
if $choosen_gun[1][1] >= 1
health = value[0] -= 1
$choosen_gun[1][1] -= 1
puts "#{key} health now is #{health}"
else
puts "Please reload your gun"
puts "Reloading......"
$choosen_gun[1][1] += 5
end
if health == 0
puts "You have defeated #{key}"
puts "Congrats!!!"
puts "We are happy for you"
puts "Lets begins to collect your prize"
CollectPrize()
else
puts "You did not defeat the #{key} yet"
end
end
In ruby constants start with Capital letter.
Methods are always defined in lower case.
Try this in irb
irb(main):001:0> def Weapon
irb(main):002:1> end
=> :Weapon
irb(main):003:0> Weapon
NameError: uninitialized constant Weapon
To solve your problem name methods using ruby's naming conventions:
zombie_room, collect_prize etc.
Then this code will work:
zombie_room(weapon())
What you are doing there is not really passing method weapon to method zombie room.
What is really going on is that method weapon is executed, then it returns a value and result of that value is passed to method zombie_room.
I think that is what you wanted.
If you need to pass a method, check out documentation for proc and lambda or just use blocks.
STDIN.read.split("\n").each do |a|
a=gets.to_i
if a >=0
puts "a is positive"
end
end
Output
C:\Ruby221-x64\programs>ruby test2.rb
5
test2.rb:3:in `read': Interrupt
from test2.rb:3:in `<main>'
Question: Why is my Ruby Code not going into the if?
Also Is the above code a way to handle continuous input? how will my code know after which input to stop. I had to press Ctrl-C to come out
Your gets call is superfluous because the STDIN.read.split("\n").each do |a| already reads the inputs into a. So remove the gets:
STDIN.read.split("\n").each do |a|
if a.to_i >= 0
puts "#{a} is positive"
end
end
Note that the I/O is buffered, and you're operating on a block basis, so you'll get:
Enter these inputs:
5
4
3
Press Ctrl-D to end the input (for Linux) or Ctrl-Z (for Windows), then you'll get results:
5 is positive
4 is positive
3 is positive
=> ["5", "4", "3"]
If you want this to be more interactive, then don't use the STDIN... each construct, but just do a loop with gets.to_i. For example:
loop do
a = gets
break if a.nil? # Exit loop on EOF (ctrl-D)
if a.to_i > 0
puts "a is positive"
end
end
If I put this into a file, say foo.rb, then I get:
OS_Prompt> ruby foo.rb
3
a is positive
5
a is positive
2
a is positive
-1
-3
2
a is positive
[ctrl-D]
OS_Prompt>
And it will quit the loop on ctrl-D since that will cause gets to return nil. Although in Windows, it might want Ctrl-Z.
▶ loop do
▷ b = STDIN.gets.to_i
▷ break if b.zero?
▷ puts b > 0 ? 'positive' : 'negative'
▷ end
#⇒ 5
# positive
# -4
# negative
# 0
# => nil
STDIN.read.split("\n").each do |a|
This line is already taking input continuously then whats the need of gets.
while(true)
b= gets.chomp.to_i
puts b >=0 ? "b is positive" : "b is negative"
end
Here chomp is used to remove \n from the input
This code:
def skip_if_three(count)
puts 'three detected, let\'s skip this loop!' if count == 3
end
5.times do |count|
skip_if_three(count)
puts count
end
returns:
0
1
2
three detected, let's skip this loop!
3 # don't want this to appear!
4
However, if utilize the next keyword and do this:
def skip_if_three(count)
next if count == 3
end
5.times do |count|
skip_if_three(count)
puts count
end
I get this SyntaxError:
Invalid next
which was sort of expected. But how do I use next from a helper?
Update
I'm using nested loops and need to execute my check in each loop, so I want to keep it DRY, hence the external method.
5.times do |i|
skip_if_three(i)
puts count
5.times do |j|
skip_if_three(j)
puts count
end
end
def skip_if_three(count)
return unless count == 3
puts "three detected, let's skip this loop!"
throw(:three)
end
5.times do |count|
catch(:three) do
skip_if_three(count)
puts count
end
end
result:
0
1
2
three detected, let's skip this loop!
4
def three?(count)
return unless count == 3
puts "three detected, let's skip this loop!"
true
end
5.times do |count|
puts count unless three?(count)
end
result:
0
1
2
three detected, let's skip this loop!
4
def three?(count)
return unless count == 3
puts "three detected, let's skip this loop!"
true
end
5.times do |count|
next if three?(count)
puts count
end
result:
0
1
2
three detected, let's skip this loop!
4
A better solution would be to redesign the code blocks so that you don't have this issue. Hiding functionality like next isn't ideal, so something like this would retain the concision of your mockup code while making it clear what's actually going on:
def is_three? count
count == 3
end
5.times do |count|
next if is_three? count
puts count
end
loop { break } can work fine, but
block = Proc.new { break }
# or
# block = lambda { break }
loop(&block) # => LocalJumpError: break from proc-closure
Is it possible to break in a block variable ?
Update:
A example to explain more:
def odd_loop
i = 1
loop do
yield i
i += 2
end
end
def even_loop
i = 2
loop do
yield i
i += 2
end
end
# This work
odd_loop do |i|
puts i
break if i > 10
end
# This doesn't work
break_greater_10 = Proc.new do |i|
puts i
break if i > 10
end
odd_loop(&break_greater_10) # break from proc-closure (LocalJumpError)
even_loop(&break_greater_10) # break from proc-closure (LocalJumpError)
As my comprehension, Proc.new should work same as block (it can return a function from block), but I don't understand why can't break a loop.
P.S. Sorry for my bad english >~<
To solve this problem you could
raise StopIteration
this worked for me.
To return from a block you can use the next keyword.
def foo
f = Proc.new {next ; p 1}
f.call
return 'hello'
end
puts foo # => 'hello' , without 1