Ruby Range printing out extra - ruby

I'm new to coding so please free to point out any errors in the way I refer to code.
rows = 5
(1..rows).each do |n|
print n, ' '
end
This prints out what I expect it to: 1 2 3 4 5.
But, when I put it into a method:
def test(rows)
(1..rows).each do |n|
print n, ' '
end
end
puts test(5)
I get 1 2 3 4 5 1..5.
Why does the 1..5 show up? And how do I get rid of it?
I need it in the method because I plan to add more code to it.

each on a Range returns the range after the looping is done, and you're probably printing the return value of test too.
Just run test(5) instead of puts test(5) or something.

Ruby always returns the last line of any function.
You are executing puts test(5), and test(5) prints the data you expect, and the extra puts prints out the data returned by test(5) method.
Hope that answers your question.

The final 1..5 is the return value from the script. You get that when you run the code in IRB. When you run that as a standalone Ruby script, it will not show up, so you do not need to worry about it.

A Ruby function will return the last statement, in your case 1..5. To illustrate I'll give it a different return value:
def test(rows)
(1..rows).each {|n| puts "#{ n } "}
return 'mashbash'
end
# Just the function invokation, only the function will print something
test(5) # => "1 2 3 4 5 "
# Same as above, plus printing the return value of test(5)
puts test(5) # => "1 2 3 4 5 mashbash"
You could write your example a little differently to achieve what you like:
def second_test(rows)
# Cast range to an array
array = (1..rows).to_a # [1, 2, 3, 4, 5]
array.join(', ') # "1, 2, 3, 4, 5", and it is the last statement => return value
end
# Print the return value ("1, 2, 3, 4, 5") from the second_test function
p second_test(5)
# => "1, 2, 3, 4, 5"

Related

Is there a more elegant way of writing a while loop in Ruby where the array size is not known?

Using the following example:
array = [1,20]
new_array = []
i = array[0]
while i < array[1]
new_array.push(i)
i+= 2
end
#new_array = [1,3,5,7,9,11,13,15,17,19]
Is there a more elegant way to write this loop without have to write an empty array (new_array) and an external variable loop counter (i)? I was thinking something along the lines of new_array.map{|x| } but instead of iterating through each element, it continually adds a number until it hits a certain limit.
Assuming your goal is to create an array of odd numbers up to a limit, you can use a range with a step.
limit = 20
array = (1..limit).step(2).to_a
EDIT
If you want to be able to descend as well as ascend you can use step.
#Ascending
start = 1
limit = 20
array = start.step(limit, 2).to_a
#Descending
start = 20
limit = 1
array = start.step(limit, -2).to_a
For the sake of having an alternative, you could also select (Enumerable#select) odds numbers (Integer#odds?) out of your Range:
(1..20).select(&:odd?)
#=> [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
My answer is addressed to the question stated in the title, not to the particular example presenteed.
Suppose the user is asked to enter a sequence of strings, with an empty string signifying that the user is finished. These strings are to be saved in an array which is to be returned.
This is a typical way of writing the code using a while loop:
def gettem
arr = []
until (s = gets.chomp).empty? do
arr << s
end
arr
end
One could instead use Kernel#loop and the keyword break, which some (including me) prefer to while and until loops.
def gettem
arr = []
loop do
s = gets.chomp
break if s.empty?
arr << s
end
arr
end
A third way, suggested by #Aleksei Matiushkin in his answer here (which I had not seen before) is the following:
def gettem
loop.with_object([]) do |_,arr|
s = gets.chomp
break arr if s.empty?
arr << s
end
end
This uses the form of loop that returns an enumerator (see the doc). If I run this and enter "dog", "cat" and "\n", the return value is ["dog", "cat"], as desired.
This approach has three of advantages over the other approaches:
the variable arr is confined to the block, away from prying eyes;
fewer lines of code are needed; and
the return value can be chained, as illustrated below.
def gettem
loop.with_object([]) do |_,arr|
s = gets.chomp
break arr if s.empty?
arr << s
end.then { |arr| [arr.size, arr] }
end
When executing this method and entering "dog", "cat" and "\n", the array [2, ["dog", "cat"]] is returned.
I've used an underscore for the first block variable (which always has a value of nil) to signify that it is not used in the block calculation.

"number" not a declared variable but still works?

I'm learning ruby, I wrote out this code as part of a course, however on line 9 the variable number is introduced but isn't declared, the console doesn't throw up an error, why is this? is it specifically part of the for loop?
#set an array counting up from 1 - 5
the_count = [1, 2, 3, 4, 5]
#array of fruits
fruits = ['apples', 'oranges', 'pears', 'apricots']
#mixed array of numbers and currency
change = [1, 'pennies', 2, 'dimes', 3, 'quarters']
# for each number in the_count array put...
for number in the_count
puts "this is count #{number}"
end
#for each element in the fruit array put...
fruits.each do |fruit|
puts "a fruit of type: #{fruit}"
end
#iterate through each element in change and on each of them preceed its value with "i got"
change.each {|i| puts "I got #{i}"}
#create an empty array
elements = []
#interate through numbers 0 - 5
(0..5).each do |i|
puts "adding to #{i} to the list."
#push each number to empty array
elements.push(i)
end
#iterate through each element in elements and preceed it with "Element was:"
elements.each {|i| puts "Element was: #{i}"}
in Ruby, variable are not declared. The interpreter decides a token is a variable if it's assigned a value or, like in this case, be used with for in syntax.

Why does my function not print the sorted array?

# Methods for calculating
# print out the input that the user entered
def PrintScores(*numbers)
numbers.each {|x| print x.join(" ")}
puts
end
#print out the scores in ascending order
def ListScores(*numbers)
numbers.sort!
print numbers
end
# Main function
out_file = File.new("out.txt", "w")
puts "Enter the scores you wish to have our stats program look into? "
user_input = gets.chomp
input_array = user_input.split(" ")
input_array.map! do |x|
x.to_i
end
PrintScores(input_array)
ListScores(input_array)
The ListScores function still prints the array in the order in which I entered it, and I can not figure out why.
The ListScores function still prints the array in the order in which I entered it and i can not figure out why?
In your current code, input_array is an instance of Array class which is passed as an argument to ListScores method. ListScores is expecting splat arguments, so numbers become an Array containing a single Array element(i.e., input_array contents). This is the reason you see the array in the same order when you try to sort it.
For example:
> user_input = gets.chomp
3 2 8 5 1
=> "3 2 8 5 1"
> input_array = user_input.split(" ")
=> ["3", "2", "8", "5", "1"]
> input_array.map! do |x|
> x.to_i
> end
=> [3, 2, 8, 5, 1]
> ListScores(input_array)
[[3, 2, 8, 5, 1]] => nil ## Notice Array with single Array element [[]]
splat operator(*) is used in methods when you have a need for variable parameter list.
In your case, you don't need splat operator in PrintScores and ListScores method.
def PrintScores(numbers) ## <-- Removed splat operator
numbers.each {|x| print x.join(" ")}
puts
end
#print out the scores in ascending order
def ListScores(numbers) ## <-- Removed splat operator
numbers.sort!
print numbers
end
Sample output:
> ListScores(input_array)
[1, 2, 3, 5, 8] => nil
NOTE: Its advisable to use snake_case for method name like list_scores instead of ListScores

Return a value from a block without returning from method

I have a class Test:
class Test
attr_accessor :data
def initialize
#data = [0, 1, 2, 3]
end
def map
#data.map!{|i| i = yield i }
end
end
and I attempt to use it like:
a = Test.new
a.map{|i|
if(i==2)
i+=1
break i #<--- -This line is the focus
else
1
end
}
puts a.data
The output I expect is [1, 1, 3, 3]. Instead, I get [1, 1, 2, 3]. The last iteration of the block in map doesn't return the modified i.
I replaced break i with next i. This performed as I expected, and produced the output [1, 1, 3, 1].
How can I modify this piece of code (or, ideally the line I point out in my second code-snippet) so that I would get the output [1, 1, 3, 3]? In other words, how can I make the block finish, but pass one last value back to map? Is there a neat and readable way to do this (besides, say, toggling a boolean flag break_now)?
I'm assuming you're asking how to leave a block and make use of the last value that was calculated rather than how to calculate a specific set of numbers; for the latter, there is probably a clever one-liner.
How about something like this:
class Test
attr_accessor :data
def initialize
#data = [0, 1, 2, 3]
end
def modify
#data.map! {|i| yield i }
end
end
a = Test.new
a.modify do |i|
break i if #done
#done = i == 2
#done ? (i + 1) : 1
end
puts a.data
An additional thought—#map is an important method in Ruby with a specific interface. In your example you're violating the interface by modifying a field in Test. For this reason I've used the name #modify instead.
In general, you could get away with this by modifying the yielded values in place. For example, if your array consisted of Strings instead of Fixnums:
class Test
attr_accessor :data
def initialize
#data = %w{a b c d}
end
def map
#data.map! { |i| yield i }
end
end
a = Test.new
a.map do |i|
if i == 'c'
i.next!
break
else
'b'
end
end
p a.data #=> ["b", "b", "d", "d"]
The problem with your example is this:
Fixnum objects have immediate value. This means that when they are assigned or passed as parameters, the actual object is passed, rather than a reference to that object. Assignment does not alias Fixnum objects. There is effectively only one Fixnum object instance for any given integer value…
Fixnums can't be altered in-place, so your expression i += 1 in the lower block doesn't affect the value of i in the upper block. That's why you get 2 in your example but d in my example.
You have to do this:
a.map{ |i| (i % 2 == 0) ? i + 1 : i }
When you use map function you don't change 'a' variable, if you want change 'a' variable do this:
a.map!{ |i| (i % 2 == 0) ? i + 1 : i }
The new value of 'i' is the value return by the block, so don't do something like:
a.map{|i| i = 1 }
because if you do:
a.map{|i| i = 1; 5 }
the result will be:
[5, 5, 5, 5]

how does collect and struct work in ruby?

Im currently going through a book and there is a pice of code that I don't quite understand:
class RevealingReferences
attr_reader :wheels
def initialize(data)
#wheels = wheelify(data)
puts data
end
def diameters
wheels.collect do |wheel|
puts "test"
wheel.rim + (wheel.tire*2)
end
end
Wheel = Struct.new(:rim, :tire)
def wheelify(data)
data.collect{|cell|
Wheel.new(cell[0], cell[1])}
end
end
end
puts RevealingReferences.new([3,2,5,8]).diameters
and I get the following output:
3
2
5
8
test
test
test
test
3
2
1
0
1) Now the 3,2,5,8 I understand, but why does not display in array format [3,2,5,8] rather its being displayed one int at a time.
2) Also, in the wheels.collect block, the output prints "test" twice before putting in the output, should it not be "test" value "test" value
3) Also, the answer 3,2,1,0 don't make any sense, when I set #wheels should wheels not be a collection of an array of 2 elements rather then 4?
1) Now the 3,2,5,8 I understand, but why does not display in array
format [3,2,5,8] rather its being displayed one int at a time.
This is due to how puts works. When it sees an array, it prints the #to_s of each element
puts [1,2,3]
# >> 1
# >> 2
# >> 3
If you want it to look like an array, you should inspect it before printing it
puts [1,2,3].inspect
# >> [1, 2, 3]
There's also a shorthand for this, the method p
p [1,2,3]
# >> [1, 2, 3]
2) Also, in the wheels.collect block, the output prints "test" twice
before putting in the output, should it not be "test" value "test"
value
The only thing printing the values is the puts statement on the return value of diameters, so they won't print until after they have been collected. If you wanted to print it after each test, you should probably do something like
def diameters
wheels.collect do |wheel|
puts "test"
p wheel.rim + (wheel.tire*2)
end
end
Which would print:
test
3
test
2
test
1
test
0
3) Also, the answer 3,2,1,0 don't make any sense, when I set #wheels
should wheels not be a collection of an array of 2 elements rather
then 4?
Based on what you're saying here, I assume your data is not in the format you intended. You're passing in [3,2,5,8], but this implies that you meant to pass in [[3,2],[5,8]], or to map across every pair of values:
def wheelify(data)
data.each_slice(2).collect do |cell|
Wheel.new(cell[0], cell[1])
end
end
The reason it isn't doing what you think is because without doing one of these, the cell variable is actually just a number. Since numbers have the brackets method defined on them, they wind up working in this case. But the brackets method just returns 1 or 0, depending on the bit (base 2) at that position:
five = 5
five.to_s(2) # => "101"
five[2] # => 1
five[1] # => 0
five[0] # => 1
So in the case of 3, wheel.rim + (wheel.tire*2) becomes
cell = 3
cell.to_s(2) # => "11"
rim = cell[0] # => 1
tire = cell[1] # => 1
rim + tire * 2 # => 3
And in the case of 2:
cell = 2
cell.to_s(2) # => "10"
rim = cell[0] # => 0
tire = cell[1] # => 1
rim + tire * 2 # => 2
And 5:
cell = 5
cell.to_s(2) # => "101"
rim = cell[0] # => 1
tire = cell[1] # => 0
rim + tire * 2 # => 1
And 8:
cell = 8
cell.to_s(2) # => "1000"
rim = cell[0] # => 0
tire = cell[1] # => 0
rim + tire * 2 # => 0
Which is why diameters returns [3, 2, 1, 0], explaining the last four digits you see.
1) puts will output each argument on a new line, or if the argument is an array, each element of an array on a new line
2) puts "test" is running in the wheels.collect block, there are four Wheel objects created so it outputs four tests while creating the diameters array.
3) The real problem is what seems like a typo either in your book or the transfer of the code to your test environment. I think that last line was meant to read
puts RevealingReferences.new([[3,2],[5,8]]).diameters
Otherwise, the Wheel.new line
Wheel.new(cell[0], cell[1])}
is calling FixNum#[] giving you the n-th bit of the integer. This was a bit of surprise to me too - it seems like a lot could go subtly wrong when accidentally supplying an integer instead of an Array.
With the original code, cell[0] and cell[1] evaluates as 3[0] and 3[1] for the first element of data. With the correction you have the array [3,2][0] => 3, and [3,2][1] => 2 which makes much more understandable code as a "collect" example.
1- collect is a iterator method that accepts a block of code.The collect iterator returns all the elements of a collection.
2- u haven't specified the value to be displayed. do "puts wheel.rim + (wheel.tire*2)".
3- if u print the 'wheel' in the collect block of diameters method, its
"#<struct RevealingReferences::Wheel rim=1, tire=1>"
"#<struct RevealingReferences::Wheel rim=0, tire=1>"
"#<struct RevealingReferences::Wheel rim=1, tire=0>"
"#<struct RevealingReferences::Wheel rim=0, tire=0>"
When the "wheel.rim + (wheel.tire*2)" statement is executed, the result is 3,2,1,0 and each result is returned. if the statement "puts wheel" is added in the collect block for diameter and the prog executed, u wont see the values (3,2,1,0) in the output.

Resources