Reputation: 81
I'm creating a short program to find the average of a given set of numbers. This is a program whose basic structure was first worked out with goto statements on a TI-83, so I'm a little unsure on proper practices with this sort of thing. Here's the code in question:
$sum = 0
$counter = 0
def average
puts "Input another number, or \"end\" to end."
input = gets.chomp
if input == "end"
puts $counter
puts ($sum / $counter).to_f
else
$sum += input.to_f
$counter += 1
puts $counter
average #Here the method is called again, repeating the cycle.
end
end
I'm not sure how else to do this, as the point of the code being built like this is that it can handle an uncertain number of inputs, hence it's repeating nature.
Upvotes: 2
Views: 584
Reputation: 8212
This isn't a new answer, but rather an illustrated example to support @adao7000's answer.
Recursion can be a really powerful tool. One of the best examples is when walking through a nested array or hash. Recursion allows you to define how to extract data once, and then use the same process at each level - without you needing to know how many levels there are.
Here is a simple example:
nested_array = [
'level 1',
[
'level 2'
],
[
[
'level 3',
[
'level 4'
]
]
]
]
def print_values(element)
if element.kind_of? Array
element.each{|e| print_values(e)}
else
puts element
end
end
print_values nested_array
Note how the print_values method either prints out the the current value or passes the sub-array back to itself.
Upvotes: 1
Reputation: 198324
Among the languages that feature so-called "tail-call optimisation", this kind of structure is the primary way of looping. Ruby, however, does not; recursion (re-entering a function that has already been entered) is commonly used, but not as a general replacement for simple looping, as in this case.
Here, as pvg states, a while
(or, equivalently, until
) loop is better:
sum = 0
counter = 0
puts "Input a number, or \"end\" to end."
input = gets.chomp
until input == "end"
sum += input.to_f
counter += 1
puts "Input another number, or \"end\" to end."
input = gets.chomp
end
puts sum / counter
Or an infinite loop with a break
:
sum = 0
counter = 0
loop do
puts "Input a number, or \"end\" to end."
input = gets.chomp
break if input == "end"
sum += input.to_f
counter += 1
end
puts sum / counter
But you could also use more Rubyish ways:
puts 'Input numbers, or "end" to end.'
array = STDIN.each_line.lazy
.map(&:chomp)
.take_while { |line| line != "end" }
.map(&:to_f)
.to_a
puts array.inject(&:+) / array.size
Or a bit more ugly but memory-efficient:
puts 'Input numbers, or "end" to end.'
sum, count = *STDIN.each_line.lazy
.map(&:chomp)
.take_while { |line| line != "end" }
.inject([0, 0]) { |memo, x|
[memo[0] + x.to_f, memo[1] + 1]
}
puts sum / count
Upvotes: 5
Reputation: 3670
Yes, a method can call itself. This is called recursion, a very important concept in programming and computer science.
All you need is a base case and the call to itself. In your example, the base case is
if input == "end"
puts $counter
puts ($sum / $counter).to_f
And the recusive step is
else
$sum += input.to_f
$counter += 1
puts $counter
average #Here the method is called again, repeating the cycle.
Upvotes: 1