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)

···

--
Posted via http://www.ruby-forum.com/.

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

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 White
Twitter: http://www.twitter.com/cwgem

···

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

Chris White wrote in post #1014080:

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 White
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.

···

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

--
Posted via http://www.ruby-forum.com/\.

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 8:33 PM, Marc Chanliau <marc.chanliau@gmail.com>wrote:

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)}"

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)

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 ( Ruby’s other ternary operator | Invisible Blocks ) 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: module Enumerable - RDoc Documentation

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

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

Adam Prescott wrote in post #1014254:

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 Chanliau > <marc.chanliau@gmail.com>wrote:

--
Posted via http://www.ruby-forum.com/\.

Chris White 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!

···

--
Posted via http://www.ruby-forum.com/\.

<snip>

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

···

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

class Factorial
def fact(n)
if n == 0
   1
else
   n * fact(n-1)
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)

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)

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

···

On Mon, Aug 1, 2011 at 10:08 PM, Marc Chanliau <marc.chanliau@gmail.com> wrote:

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)}"

--
Phillip Gawlowski

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

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)

···

On Mon, Aug 1, 2011 at 3:08 PM, Marc Chanliau <marc.chanliau@gmail.com>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!

Phillip Gawlowski wrote in post #1014267:

···

On Mon, Aug 1, 2011 at 10:08 PM, Marc Chanliau <marc.chanliau@gmail.com> > wrote:

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

Let's fix your code:

Thanks, very useful suggestions!

--
Posted via http://www.ruby-forum.com/\.

Josh Cheek wrote in post #1014269:

···

On Mon, Aug 1, 2011 at 3:08 PM, Marc Chanliau > <marc.chanliau@gmail.com>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.

--
Posted via http://www.ruby-forum.com/\.