tags:

views:

219

answers:

4

I am a ruby newbie and have a problem with getting the code below to work

def factorial(n)
  if n == 0
    1
  else
    n * factorial(n-1)
  end
end

puts factorial(numbers)

I keep getting the error message can't convert Fixnum into Array (TypeError) in `factorial'. Anyone able to help me with answer to what I am doing wrong in this case? Thanks in advance

+3  A: 

I don't know any Ruby, but I suspect that numbers is an array of numbers, not just one, and when you pass it to factorial it tries to perform the computation and blows up. (It doesn't make sense to compare an array to zero, multiply it, or subtract it.)

You'll need to either change factorial to accept multiple numbers and find the factorial of each, or (easier) change the calling code to compute one factorial at a time over the set of numbers.

mquander
I just busted out irb to test this. If numbers is an array, it will give this error, indeed. It complained about the - method, though. Ruby doesn't care what types are compared with ==, an array will just never equal 0, so it moved on and tries to subtract 1 from your array and blows up there.You want to iterate over your array on calling, probably, like this:numbers.each do |number| puts factorial numberendYou could write a little wrapper method, if you really wanted. Or if you want an array back out, you could do...puts numbers.collect { |number| factorial number }
Ben Hamill
Yeah, I realized based on the error that my initial idea (it was blowing up when comparing) was probably not correct and it was likely later on during the multiplication or subtraction, so I edited my answer to hedge my bets ;)
mquander
Well, [1, 2, 3] * 3 returns [1, 2, 3, 1, 2, 3, 1, 2, 3] in Ruby, but your point stands.
Ben Hamill
+5  A: 

As mquander says, numbers is apparently an array.

So what you need is something like:

puts(numbers.map { |n| factorial(n) })

And, incidentally, your error has nothing to do with recursion.

Confirming mquander's theory:

> numbers = [1,2,3]
> puts factorial(numbers)
TypeError: can't convert Fixnum into Array

And just out of curiosity, here's how you implement factorial in ruby without recursion, so that you don't get a stack overflow for large numbers.

def factorial(n)
  (1..n).inject(1) { |ac, x| ac * x }
end
kch
A: 

you are probably looking for something like:

numbers.map { |n| factorial(n) }
JasonTrue
A: 

As an addition to kch's code, you can modify the function to handle both array and scalar input.

def factorial(n)
  if n.is_a?(Array)
    return n.map {|n| factorial(n)}
  end
  raise unless n.is_a?(Integer) and (n >= 0)
  (n == 0) ? 1 : n * factorial(n-1)
end

This functions handles scalars, arrays, nested arrays, invalid things, etc.

Edit: Incorporated kch's simplified type-checking code

bta
Instead of trying to handle all sorts of unexpected input, it's better to actually ascertain that you are getting a number, in fact, a positive integer, which is the correct domain for the factorial function. So, `raise unless n.is_a?(Integer) and n >= 0`
kch
Since the submitter was feeding an Array into his function, I assumed that the function needed to handle such input. If Array support is not needed then yes, your solution is a much cleaner one (I always forget about `raise`).
bta
Now that I re-think about it, type checking should not be needed. A function like this would be implemented as a member function and classes like `Integer` would be extended to include it (so you would call it with something like `x=212.factorial`), so the argument would be implicit and of a guaranteed type.
bta