Newbie question

This snippet works fine:
def fact(n)
if n == 0
1
else
n * fact(n-1)
end
end

result = fact(4)
puts "The factorial of the number 4 is: " + result.to_s

Now, I want to make the snippet above interactive and allow the user to
enter a number and get in return the factorial for that number. The
following snippet doesn’t work, what am I doing wrong?

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

puts "enter a positive integer: "
n = gets
puts "factorial: " + fact(n)

On Jul 31, 2011, at 4:41 PM, Marc C. wrote:

end

puts "enter a positive integer: "
n = gets

Problem one, your factorial function is expecting to work with an
integer value, but gets returns a string. Instead you can convert it
straight from gets:

n = gets.to_i

puts "factorial: " + fact(n)

Next the return value of fact is an integer value, but you’re using + to
concat what is expected to be two strings. Instead you can inline the
fact call, which will call .to_s on the result in the background:

puts “factorial: #{fact(n)}”

Here’s the full code:

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

puts "enter a positive integer: "
n = gets.to_i
puts “factorial: #{fact(n)}”

Regards,
Chris W.
Twitter: http://www.twitter.com/cwgem

Chris W. wrote in post #1014080:

On Jul 31, 2011, at 4:41 PM, Marc C. wrote:

end

puts "enter a positive integer: "
n = gets

Problem one, your factorial function is expecting to work with an
integer value, but gets returns a string. Instead you can convert it
straight from gets:

n = gets.to_i

puts "factorial: " + fact(n)

Next the return value of fact is an integer value, but you’re using + to
concat what is expected to be two strings. Instead you can inline the
fact call, which will call .to_s on the result in the background:

puts “factorial: #{fact(n)}”

Here’s the full code:

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

puts "enter a positive integer: "
n = gets.to_i
puts “factorial: #{fact(n)}”

Regards,
Chris W.
Twitter: http://www.twitter.com/cwgem

Thanks, that helps a lot.
Now, I want to put that code into a class (in good Ruby fashion, I
guess).
I have this code:

class Factorial
def fact(n)
if n == 0
1
else
n * fact(n-1)
end
end
end

my_fact = Factorial.new
puts "enter a positive integer: "
my_fact = gets.to_i
puts “factorial: #{fact(n)}”

I get this error:

marcc$ ruby factorial7.rb
enter a positive integer: 4
factorial7.rb:14:in <main>': undefined local variable or method n’ for
main:Object (NameError)

thanks again in advance.

my_fact = Factorial.new

There’s two issues here. The first is that you renamed n to my_fact,
so it becomes undeclared as the error mentions. The other is that since
fact is now part of the Factorial class, you need to change how fact()
is called:

puts “factorial: #{my_fact.fact(n)}”

More interesting stuff to help you along the learning path. Right now in
order to access your fact function, you have to create a new instance of
the class. Instead you can utilize what is known as a singleton method.
So the class can be rewritten as such:

class Factorial
def Factorial.fact(n)
if n == 0
1
else
n * fact(n-1)
end
end
end

puts Factorial.fact(10)

Now we don’t have to create a new instance just to call the method, and
the method still remains encapsulated in the Factorial class for
organizational purposes. Next up is to make the code a bit shorter:

class Factorial
def Factorial.fact(n)
(n == 0) ? 1 : n * fact(n-1)
end
end

puts Factorial.fact(10)

This uses the ternary operator (

) which has the format:

(condition to test) ? if condition is true run this : if condition is
false run this

Finally for something even more advanced, you can use inject and kill
the need for a recursive call all together:

class Factorial
def Factorial.fact(n)
(1…n).inject(1, :*)
end
end

puts Factorial.fact(10)

More information on inject can be found here:
http://www.ruby-doc.org/core/classes/Enumerable.html#M001494

I’d recommend reading it over and write some code that uses it. It is a
very powerful tool!

Regards,
Chris W.
Twitter: http://www.twitter.com/cwgem

Adam P. wrote in post #1014254:

On Mon, Aug 1, 2011 at 8:33 PM, Marc C.
[email protected]wrote:

my_fact = Factorial.new
puts "enter a positive integer: "
my_fact = gets.to_i
puts “factorial: #{fact(n)}”

You’re relying on n being defined here, but it’s only defined inside
Factorial#fact, which is why you get “undefined local variable or method
`n’” as an error message. You probably wanted to do this:

puts “factorial: #{fact(my_fact)}”

As a perhaps neater way of writing the #fact method, I’d do it like this
(although it’s moot, really):

def fact(n)
return 1 if n == 0

n * fact(n-1)
end

Thanks, I took your suggestions into account. Now I have this class:

class Factorial
def fact(n)
return 1 if n == 0
n * fact(n-1)
end
end

my_fact = Factorial.new
print "enter a positive integer: "
my_fact = gets.to_i
puts “factorial: #{fact(my_fact)}”

But I still get an error:
marcc$ ruby factorial7.rb
enter a positive integer: 4
factorial7.rb:11:in <main>': undefined method fact’ for main:Object
(NoMethodError)

thanks!

On Mon, Aug 1, 2011 at 8:33 PM, Marc C.
[email protected]wrote:

my_fact = Factorial.new
puts "enter a positive integer: "
my_fact = gets.to_i
puts “factorial: #{fact(n)}”

You’re relying on n being defined here, but it’s only defined inside
Factorial#fact, which is why you get “undefined local variable or method
`n’” as an error message. You probably wanted to do this:

puts “factorial: #{fact(my_fact)}”

As a perhaps neater way of writing the #fact method, I’d do it like this
(although it’s moot, really):

def fact(n)
return 1 if n == 0

n * fact(n-1)
end

On Mon, Aug 1, 2011 at 10:08 PM, Marc C. [email protected]
wrote:

my_fact = gets.to_i
puts “factorial: #{fact(my_fact)}”

Let’s fix your code:

class Factorial

Same as your original.

end

factorial = Factorial.new # Creating a new Factorial instance
print "enter a positive integer: "
num = gets.to_i # Assigning input to its own variable!
puts “Factorial: #{factorial.fact(num)}” # calling the fact-method of
your Factorial instance


Phillip G.

phgaw.posterous.com | twitter.com/phgaw | gplus.to/phgaw

A method of solution is perfect if we can forsee from the start,
and even prove, that following that method we shall attain our aim.
– Leibniz

On Mon, Aug 1, 2011 at 3:08 PM, Marc C.
[email protected]wrote:

But I still get an error:
marcc$ ruby factorial7.rb
enter a positive integer: 4
factorial7.rb:11:in <main>': undefined method fact’ for main:Object
(NoMethodError)

thanks!

I think it is admirable that so many people are willing to take the time
and
effort to help newbies. But I think in this case, the help he needs
isn’t
for you to tell him what is wrong / why it is wrong, but to explain how
you
figured it out.

The error message here is pretty straightforward, it even tells you
where
your program experienced the problem (the line number). Marc, you need
to
study this error message, you will experience it many times, so lets
deal
with it now.

It says “undefined method fact' for main:Object" When you reference something in Ruby (you write some text that isn't a string/symbol/number/etc) then the interpreter goes looking for either a local variable (such as my_fact) or a method (such as fact, which you defined in the Factorial class). In this case, at the top level, methods get defined on Object, and the toplevel is an instance of Object, so that is where it goes looking for methods. Since you have no local variables or methods on Object named fact, the interpreter raises an error saying "undefined method fact’ for main:Object” (in this case, it doesn’t say
anything about local variables, because it realizes that you passed an
argument to fact, so it must be a method).

Now that you know what is wrong, look at where the error happened in
your
code to get the context. The error tells you that it was raised in
“factorial7.rb:11” So in your file named factorial7.rb, on line 11.

Going there, you can see that you have the line puts "factorial: #{fact(my_fact)}" Now that we have context, think about what you were
intending, and what the interpreter was expecting. Why don’t they align,
and
what can you do about it?

(and I see that Phillip has already showed you how to fix it, and you’ve
already responded, but I’m going to post this anyway, because I think it
is
imperative that you learn this)

Phillip G. wrote in post #1014267:

On Mon, Aug 1, 2011 at 10:08 PM, Marc C. [email protected]
wrote:

my_fact = gets.to_i
puts “factorial: #{fact(my_fact)}”

Let’s fix your code:

Thanks, very useful suggestions!

Chris W. wrote in post #1014256:

Finally for something even more advanced, you can use inject and kill
the need for a recursive call all together:

class Factorial
def Factorial.fact(n)
(1…n).inject(1, :*)
end
end

puts Factorial.fact(10)

Great stuff. Now I’m using the ternary operator in my class, with a user
prompt as follows, which works well:

class Factorial
def Factorial.fact(n)
(n == 0) ? 1 : n * fact(n-1)
end
end

print "Enter a positive integer: "
n = gets.to_i
puts “The factorial of #{n} is: #{Factorial.fact(n)}”

I will get to the “inject” thing later. What I want to do now is to use
exception handling (e.g., in case the user enters the wrong input).

Thanks again for the suggestions, that help a lot!

On 08/01/2011 12:50 PM, Chris W. wrote:

my_fact = Factorial.new

 else
   n * fact(n-1)
 end

end
end

puts Factorial.fact(10)

Interesting. I usually see this written as

class Factorial
def self.fact(n)

(or using class << self)

Also, it seems more natural to have Factorial be a module instead of a
class, but that’s not something the OP needs to worry about at this
point.

-Justin

Josh C. wrote in post #1014269:

On Mon, Aug 1, 2011 at 3:08 PM, Marc C.
[email protected]wrote:

But I still get an error:
marcc$ ruby factorial7.rb
enter a positive integer: 4
factorial7.rb:11:in <main>': undefined method fact’ for main:Object
(NoMethodError)

thanks!

I think it is admirable that so many people are willing to take the time
and
effort to help newbies. But I think in this case, the help he needs
isn’t
for you to tell him what is wrong / why it is wrong, but to explain how
you
figured it out.

Josh, I really appreciate the explanation. I pasted it in my program (as
a comment) following the errors incurred in the original program.
I’m using an intro book to Ruby + the doc, so I’ll get there eventually,
but I like your (more “didactic”) approach!
Thanks.