Using `each` rather than `for`-`each` loop - ruby

Learn Ruby the Hard Way asks to rewrite a script:
i = 0
numbers = []
while i < 6
puts "At the top i is #{i}"
numbers.push(i)
i += 1
puts "Numbers now: ", numbers
puts "At the bottom i is #{i}"
end
puts "The numbers: "
numbers.each {|num| puts num}
using for-loops and the (0 .. 6) range. The only solution I can find to work uses the for-each construct, which the author says to avoid:
def range_loop(increment, upper_limit)
numbers = []
for number in (0...upper_limit)
puts "The number is : #{number}"
numbers.push(number)
end
puts "The numbers: "
for number in numbers
puts number
end
end
range_loop(1, 6)
How can I write this script using the each construct?

You can use Range object and Enumerable#each method for this goal:
(0...6).each do |i|
#some code here
end

You could also use upto
0.upto(6) {|x| p "The number is #{x}"}

Related

Trying to convert each to while loop, creates TypeError. Why is it having a problem with symbols?

I have a finished program, but now I need to convert an #Each loop to a #While loop. The loop should output almost the same information, but it throws me a 'directory.rb:24:in `print': no implicit conversion of Symbol into Integer (TypeError)' instead.
def input_students
puts "Please enter the names of the students"
puts "To finish, just hit return twice"
students = []
name = gets.chomp
while !name.empty? do
students << {name: name, cohort: :november}
puts "Now we have #{students.count} students"
name = gets.chomp
end
students
end
students = input_students
def print_header
puts "The students of Villains Academy"
puts "----------"
end
def print(students)
students.each.with_index(1) do |students, index|
puts "#{index} #{students[:name]}, #{students[:cohort]} cohort"
end
end
def print_footer(names)
puts "Overall we have #{names.count} great students"
end
print_header
print(students)
print_footer(students)
Works as expected. I'm trying:
def print(students)
i = 0
while i < students.length
puts "#{students[:name]}, #{students[:cohort]} cohort"
end
end
Why doesn't the #While loop work with similar input, and why is it trying to convert to an integer?
Because your #each loop was shadowing the students variable:
# v v
students.each.with_index(1) do |students, index|
puts "#{index} #{students[:name]}, #{students[:cohort]} cohort"
end
you iterate an array called students and then assign each element in the array to a variable named students. When you get rid of the each loop, you didn't change the block to stop looking at students, so it's now looking at the array. To get a single element try:
def print(students)
i = 0
while i < students.length
puts "#{students[i][:name]}, #{students[i][:cohort]} cohort"
end
end
while i < students.length
puts "#{students[:name]}, #{students[:cohort]} cohort"
end
students is an array. You can't address its elements with symbols. What you need to do is use i to fetch an element of students. You can call [:name] on that.
The mistake comes, I think, from poor naming in this snippet. And/or not understanding how each works.
students.each.with_index(1) do |students, index|
# ^^^^^^
# This here is called `students`, but its value is a single student,
# not a collection of students.

For loops while i>10 in Ruby

To set i and its maximum and have a double iterator In Java, I would use for loops as follows:
for (i=1; i<10, i+=){
for (j=1; j<10; j++){
puts i (or whatever function)
}
}
I'm trying to figure out how to do this in Ruby. The for loops I've seen in Ruby are for ranges, and single iterator:
for i in (1..10)
puts i
end
or while loops
i = 1
while i < 10
puts i
i += 1
end
Are these while loops the (single iterator) Ruby equivalent of the Java I mentioned, or is there another way to do these for loops?
The "ruby-ish" way would be each
(1...10).each do |i|
puts i
end
or to double iterate
(1...10).each do
(1...10).each do |j|
puts j
end
end
As #maxwilliams and #sawa point out, (1...10) is 1 through 9 inclusive, (1..10) is 1 through 10 inclusive.
Why not do a nested loop?
for i in 1...10
for j in 1...10
puts i
end
end
By the way, the Ruby equivalent is for i in 1...10, not for i in 1..10.
In Ruby you rarely use the for loop. Actually, you never use it. In Ruby you use enumerators and blocks.
for (i=1; i<10, i+=){
for (j=1; j<10; j++){
puts i (or whatever function)
}
}
is equivalent to
(1...10).each do |i|
(1...10).each do |j|
puts j
end
end
Also note there is a difference between
(1..10).each do |i|
end
and
(1...10).each do |i|
end
The latter is equivalent to
(1..9).each do |i|
end
In fact, the .. will create an inclusive range whereas ... an exclusive range.
It is also worth to mention that in Ruby you rarely use indexes to loop items. Therefore, if your goal is to print out some elements in an array, your code should not be
(1..10).each do |i|
p #items[i]
end
rather you call each on the collection.
#items.each do |item|
p item
end
In certain cases, like in your question, you don't even need a range (when your loop starts from 1. You can use times.
9.times do |i|
9.times do |j|
puts j
end
end
In Ruby we don't use parenthesis. They are uncool.
This is an example for while statement in Ruby
while $i < $num do
puts "Value for i is #{i}"
$i +=1
end
And this is an example for for loop.
for i in 0..10
puts "Value of local variable is #{i}"
end
But there are more loops! Check this for more info http://www.tutorialspoint.com/ruby/ruby_loops.htm
But if you are Java programmer, you can check this link https://www.ruby-lang.org/en/documentation/ruby-from-other-languages/to-ruby-from-java/

Why is my Ruby Code not processing the input?

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

Ruby function to check if a number is divisible by five and is even

def is_even?(n)
remainder_when_divided_by_2 = n % 2
if remainder_when_divided_by_2 == 0
return true
else
return false
end
end
def is_odd?(n)
return ! is_even?(n)
end
puts "1 is_even? #{is_even?(1)} - is_odd? #{is_odd?(1)}"
puts "2 is_even? #{is_even?(2)} - is_odd? #{is_odd?(2)}"
puts "3 is_even? #{is_even?(3)} - is_odd? #{is_odd?(3)}"
puts "4 is_even? #{is_even?(4)} - is_odd? #{is_odd?(4)}"
puts "5 is_even? #{is_even?(5)} - is_odd? #{is_odd?(5)}"
puts "6 is_even? #{is_even?(6)} - is_odd? #{is_odd?(6)}"
def is_even_and_divisible_by_five?(n)
remainder_when_divided_by_five = n % 5
if (remainder_when_divided_by_five == 0) && (is_even?(n) == true)
return true
else
return false
end
end
puts "5 is_even_and_divisible_by_five? #{is_even_and_divisible_by_five?(5)}"
puts "10 is_even_and_divisible_by_five? #{is_even_and_divisible_by_five?(10)}"
puts "15 is_even_and_divisible_by_five? #{is_even_and_divisible_by_five?(15)}"
puts "20 is_even_and_divisible_by_five? #{is_even_and_divisible_by_five?(20)}"
puts "25 is_even_and_divisible_by_five? #{is_even_and_divisible_by_five?(25)}"
puts "30 is_even_and_divisible_by_five? #{is_even_and_divisible_by_five?(30)}"
The problem was I had not called the method is_even_and_divisible_by_five in the puts commands at the bottom of the code. I called it is_even_and_divisble_by_5. Then in the if statement in the is_even_and_divisble_by_five method, I left of the (n) arguement from Is_even. Thank you all very much!
Even (divisible by two) and divisible by five also means "divisible by ten":
def is_even_and_divisible_by_five?(n)
n % 10 == 0
end
You called
is_even_and_divisible_by_5?
instead of
is_even_and_divisible_by_five?
Also is_even? function is undefined. I guess there was some mistake made with its defining or maybe even not-defining. So maybe when you defined is_even_and_divisible_by_five?(n) function there were some other errors and It was not defined too. Plus I think here is much easier solution:
def is_even_and_divisible_by_five?(n)
n % 5 == 0 && n.even?
end
In Ruby You don't have to use return all the time. You should use it quite rarely. The reason is ruby functions return last calculated value by default. And nearly everything is returning value in ruby, even blocks and If-Else statements. If you open irb console and try to do some code, for example:
a = 5
=> 5
Second line is what first line returns. You can do some experiments like this by your own with any type of conditions you like.
The name of your method is is_even_and_divisible_by_five?, not is_even_and_divisible_by_5?.
is_even? is not defined by itself
Here a shorter version of your method
def is_even_and_divisible_by_five? n
0 == n % 5 + n % 2
end

Passing arguments to functions in ruby using variables

Following the tutorial Zed A. Shaw, I'm writing a soccer game. My code so far is as follows:
$team_a_players = ["Basar", "Mehmet", "Abdullah", "Alpaslan", "Salih", "Recep", "Ibrahim", "Orhan", "Hakki", "Yakup", "Serdar"]
$team_a_substitutes = ["Hasan", "Turgay", "Umit"]
$team_b_players = ["Habib", "Erkan", "Sahin", "Cemal", "Ahmet", "Fikret", "Yucel", "Pergel", "Ali", "Sabri", "Yilmaz"]
$team_b_substitutes = ["Abdulkadir", "Gokhan", "Mustafa"]
$yellow = []
$red = []
$reasons = ["corner", "direct attack", "free kick", "side attack", "speed kick"]
$team_a_attack = 90.0
$team_a_defense = 80.0
$team_b_attack = 70.0
$team_b_defense = 60.0
$team_a_goals = 0
$team_b_goals = 0
def prompt()
print "> "
end
def dice()
if rand(2) == 0
round_team_a()
else
round_team_b()
end
end
def fauls()
if rand(0) > 0.95 and rand(10) % 2 == 0
faul_player = $team_a_players[rand(11)]
if $yellow.include?(faul_player)
$red.push(faul_player)
$team_a_players.delete("faulplayer")
puts "#{faul_player} of Team A gets a red card in #{$i}. minute!"
puts "Who would you like to substitute in place of #{faul_player}?"
list_subs_a()
prompt()
substitute = STDIN.gets.chomp()
$team_a_players.push("substitute")
$yellow.delete(faul_player)
else
$yellow.push(faul_player)
puts "#{faul_player} of Team A gets a yellow card in #{$i}. minute!"
end
elsif rand(0) > 0.95 and rand(10) % 2 == 1
faul_player = $team_b_players[rand(11)]
if $yellow.include?(faul_player)
$red.push(faul_player)
$team_b_players.delete("faulplayer")
puts "#{faul_player} of Team B gets a red card in #{$i}. minute!"
puts "Who would you like to substitute in place of #{faul_player}?"
list_subs_b()
prompt()
substitute = STDIN.gets.chomp()
$team_b_players.push("substitute")
$yellow.delete(faul_player)
else
$yellow.push(faul_player)
puts "#{faul_player} of Team B gets a yellow card in #{$i}. minute!"
end
else
faul_player = nil
end
end
def list_subs_a()
$team_a_substitutes.each {|p| puts p}
end
def list_subs_b()
$team_b_substitutes.each {|p| puts p}
end
def list_yellow()
$yellow.each {|p| puts p}
end
def list_red()
$red.each {|p| puts p}
end
def round_team_a()
score = $team_a_attack / $team_b_defense * rand(0)
if score > 1
goal = 1
$team_a_goals += 1
reason = $reasons[rand(5)]
puts "Team A scored #{goal} goal through a #{reason} in #{$i}. minute!"
else
goal = 0
end
end
def round_team_b()
score = $team_b_attack / $team_a_defense * rand(0)
if score > 1
goal = 1
$team_b_goals += 1
reason = $reasons[rand(5)]
puts "Team B scored #{goal} goal through a #{reason} in #{$i}. minute!"
else
goal = 0
end
end
def match()
$i = 0
until $i > 59 do
dice()
fauls()
$i += 1
end
puts "Team A scored a total of #{$team_a_goals} goals and Team B scored a total of #{$team_b_goals} goals."
if $team_a_goals > $team_b_goals
puts "Team A won against Team B by #{$team_a_goals}:#{$team_b_goals}!"
elsif $team_b_goals > $team_a_goals
puts "Team B won against Team A by #{$team_b_goals}:#{$team_b_goals}!"
else
puts "It's a tie!"
end
if $yellow.length > 0
puts "Players shown a yellow card are:"
list_yellow()
else
puts "No yellow cards in the end of the game"
end
if $red.length > 0
puts "Players shown a red card are:"
list_red()
else
puts "No red cards in the end of the game"
end
end
match()
From here, I would like to do the following:
Replace the arrays $yellow and $red with hashes so that I can also report minutes and teams of yellow- and red-cards.
Replace the arrays starting with the name $team_ with hashes so that I can add individualized attack- and defense-powers to players so that substitutions mean sth. but before the code gets any more complex, I have to solve sth. This looks similar to this question concerning php.
Define the functions list, round and faul in a way that can be used common to a_players and b_players . I tried doing team_#{team}_players instead of team_a_players etc, but cannot achieve it.
What I seek is a guide to that problem, not a solution, so that I can fix this myself. A link or long explanation in clear words is very much more welcome than a fixed code. And please note that the tutorial has not mentioned classes yet, so this is not an option yet.
The basic idea you haven't seemed to grasp is passing arguments to functions.
Assume we have two global variables and we wish to perform identical operations on them - say multiply elements of arrays by 2.
You write:
$a = [1,2,3]
$b = [2,3,4]
def multiply_a
result = []
for element in $a do
result << element * 2
end
result
end
def multiply_b
result = []
for element in $b do
result << element * 2
end
result
end
But this code is very bad. First of all, you should note that in Ruby $a is a special variable - a global variable. You should never need to use them - writing code containing them means that there is something wrong with it. So how to fix it?
The basic idea is to pass an argument to a function, instead of hard coding the variable, the function operates on. So you can transform the code as follows:
a = [1, 2, 3]
b = [2, 3, 4]
def multiply(argument)
result = []
for element in argument do
result << element * 2
end
result
end
# and then call
multiply(a) # instead of multiply_a
multiply(b) # instead of multiply_b
This is the way you should fix your code.

Resources