Chesshunter
Chesshunter

Reputation: 81

Is it bad practice to call a method within itself?

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

Answers (3)

ReggieB
ReggieB

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

Amadan
Amadan

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

adao7000
adao7000

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

Related Questions