I'm trying to write program that computes factorial. However, when I try to run the code below, I get this error: undefined method `*' for nil:NilClass (NoMethodError)
1.upto(number) {|x| a = x==1 ? 1 : a*x }
Am I setting up the ternary operator incorrectly, or is something else wrong?
Thanks for the help
Your ternary operator is set up correctly, but your variable a is not defined at the point where you do the multiplication.
This will work because b has a value:
b = 5
1.upto(number) {|x| a = x==1 ? 1 : b*x }
I'd do it something like:
def factorial(number)
(2 .. number).inject(1) { |m, n| m * n }
end
factorial(1) # => 1
factorial(2) # => 2
factorial(3) # => 6
factorial(5) # => 120
inject is a useful method for things like this, and isn't limited to use with numbers.
It can be written even more concisely:
(1..n).inject(:*) || 1
which comes from "Ruby factorial function". That'll give you something to chew on for a while.
Your code is doing several things that aren't correct:
a isn't ever defined prior to running, so a*x is doomed to fail because you can't multiply a nil by x.
upto will pass the current value into the block as x, but assigning to a will fail because it's always a new local variable a. There is no static memory of a unless you define it outside the block's scope.
In a lot of Ruby iterators, the value of the block can be used as a return value. upto doesn't work that way. You'll get the seed value returned, so, you'd get back 1.
Working out these sort of problems is best done using IRB, which comes with Ruby. Inside the interactive session you can try variations on your code to see what works. It's a lot faster/easier and more convenient than trying to write a script and go through the edit/run cycle.
What about this:
factorial = Hash.new{ |x,y| x[y]= y<2 ? 1 : x[y-1]*y }
Let's test that out by dropping into IRB to see what it does:
>> factorial = Hash.new{ |x,y| x[y]= y<2 ? 1 : x[y-1]*y }
{}
>> factorial[1]
1
>> factorial
{
1 => 1
}
>> factorial[2]
2
>> factorial
{
1 => 1,
2 => 2
}
>> factorial[100]
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
>> factorial
{
1 => 1,
2 => 2,
3 => 6,
4 => 24,
5 => 120,
6 => 720,
7 => 5040,
8 => 40320,
9 => 362880,
10 => 3628800,
...
100 => 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
}
YOW! You're going to compute every intermediate value!? It's going to rapidly consume memory, with very little advantage.
Related
I want to check if the number is even!
I tried the following:
a = 4.0
a.is_a? Integer
=> false
a.even?
=> undefined method for Float
So how can i check if the number is even?
If you are unsure if your variable has anything after the decimal and would like to check before converting to integer to check odd/even, you could do something like this:
a = 4.6
b = 4.0
puts a%1==0 && a.to_i.even? #=> false
puts b%1==0 && a.to_i.even? #=> true
Additionally, if you want to create an even? method for the Float class:
class Float
def even?
self%1==0 && self.to_i.even?
end
end
a = 4.6
b = 4.0
a.even? #=> false
b.even? #=> true
Make it an Integer then:
a = 4.0
a.to_i == a && a.to_i.even? #=> true
Just keep in mind how numbers are converted:
(4.0).to_i # same as Integer(4.0)
=> 4
(4.5).to_i
=> 4
(4.9).to_i
=> 4
Using round may be safer:
(4.0).round
=> 4
(4.5).round
=> 5
(4.9).round
=> 5
Then of course you can call even as #Yu Hao wrote:
(4.5).round.even?
=> false
You can also easily observe known floats 'feature':
(4.499999999999999).round.even?
=> true
(4.4999999999999999).round.even?
=> false
I'm new to coding so please free to point out any errors in the way I refer to code.
rows = 5
(1..rows).each do |n|
print n, ' '
end
This prints out what I expect it to: 1 2 3 4 5.
But, when I put it into a method:
def test(rows)
(1..rows).each do |n|
print n, ' '
end
end
puts test(5)
I get 1 2 3 4 5 1..5.
Why does the 1..5 show up? And how do I get rid of it?
I need it in the method because I plan to add more code to it.
each on a Range returns the range after the looping is done, and you're probably printing the return value of test too.
Just run test(5) instead of puts test(5) or something.
Ruby always returns the last line of any function.
You are executing puts test(5), and test(5) prints the data you expect, and the extra puts prints out the data returned by test(5) method.
Hope that answers your question.
The final 1..5 is the return value from the script. You get that when you run the code in IRB. When you run that as a standalone Ruby script, it will not show up, so you do not need to worry about it.
A Ruby function will return the last statement, in your case 1..5. To illustrate I'll give it a different return value:
def test(rows)
(1..rows).each {|n| puts "#{ n } "}
return 'mashbash'
end
# Just the function invokation, only the function will print something
test(5) # => "1 2 3 4 5 "
# Same as above, plus printing the return value of test(5)
puts test(5) # => "1 2 3 4 5 mashbash"
You could write your example a little differently to achieve what you like:
def second_test(rows)
# Cast range to an array
array = (1..rows).to_a # [1, 2, 3, 4, 5]
array.join(', ') # "1, 2, 3, 4, 5", and it is the last statement => return value
end
# Print the return value ("1, 2, 3, 4, 5") from the second_test function
p second_test(5)
# => "1, 2, 3, 4, 5"
Im currently going through a book and there is a pice of code that I don't quite understand:
class RevealingReferences
attr_reader :wheels
def initialize(data)
#wheels = wheelify(data)
puts data
end
def diameters
wheels.collect do |wheel|
puts "test"
wheel.rim + (wheel.tire*2)
end
end
Wheel = Struct.new(:rim, :tire)
def wheelify(data)
data.collect{|cell|
Wheel.new(cell[0], cell[1])}
end
end
end
puts RevealingReferences.new([3,2,5,8]).diameters
and I get the following output:
3
2
5
8
test
test
test
test
3
2
1
0
1) Now the 3,2,5,8 I understand, but why does not display in array format [3,2,5,8] rather its being displayed one int at a time.
2) Also, in the wheels.collect block, the output prints "test" twice before putting in the output, should it not be "test" value "test" value
3) Also, the answer 3,2,1,0 don't make any sense, when I set #wheels should wheels not be a collection of an array of 2 elements rather then 4?
1) Now the 3,2,5,8 I understand, but why does not display in array
format [3,2,5,8] rather its being displayed one int at a time.
This is due to how puts works. When it sees an array, it prints the #to_s of each element
puts [1,2,3]
# >> 1
# >> 2
# >> 3
If you want it to look like an array, you should inspect it before printing it
puts [1,2,3].inspect
# >> [1, 2, 3]
There's also a shorthand for this, the method p
p [1,2,3]
# >> [1, 2, 3]
2) Also, in the wheels.collect block, the output prints "test" twice
before putting in the output, should it not be "test" value "test"
value
The only thing printing the values is the puts statement on the return value of diameters, so they won't print until after they have been collected. If you wanted to print it after each test, you should probably do something like
def diameters
wheels.collect do |wheel|
puts "test"
p wheel.rim + (wheel.tire*2)
end
end
Which would print:
test
3
test
2
test
1
test
0
3) Also, the answer 3,2,1,0 don't make any sense, when I set #wheels
should wheels not be a collection of an array of 2 elements rather
then 4?
Based on what you're saying here, I assume your data is not in the format you intended. You're passing in [3,2,5,8], but this implies that you meant to pass in [[3,2],[5,8]], or to map across every pair of values:
def wheelify(data)
data.each_slice(2).collect do |cell|
Wheel.new(cell[0], cell[1])
end
end
The reason it isn't doing what you think is because without doing one of these, the cell variable is actually just a number. Since numbers have the brackets method defined on them, they wind up working in this case. But the brackets method just returns 1 or 0, depending on the bit (base 2) at that position:
five = 5
five.to_s(2) # => "101"
five[2] # => 1
five[1] # => 0
five[0] # => 1
So in the case of 3, wheel.rim + (wheel.tire*2) becomes
cell = 3
cell.to_s(2) # => "11"
rim = cell[0] # => 1
tire = cell[1] # => 1
rim + tire * 2 # => 3
And in the case of 2:
cell = 2
cell.to_s(2) # => "10"
rim = cell[0] # => 0
tire = cell[1] # => 1
rim + tire * 2 # => 2
And 5:
cell = 5
cell.to_s(2) # => "101"
rim = cell[0] # => 1
tire = cell[1] # => 0
rim + tire * 2 # => 1
And 8:
cell = 8
cell.to_s(2) # => "1000"
rim = cell[0] # => 0
tire = cell[1] # => 0
rim + tire * 2 # => 0
Which is why diameters returns [3, 2, 1, 0], explaining the last four digits you see.
1) puts will output each argument on a new line, or if the argument is an array, each element of an array on a new line
2) puts "test" is running in the wheels.collect block, there are four Wheel objects created so it outputs four tests while creating the diameters array.
3) The real problem is what seems like a typo either in your book or the transfer of the code to your test environment. I think that last line was meant to read
puts RevealingReferences.new([[3,2],[5,8]]).diameters
Otherwise, the Wheel.new line
Wheel.new(cell[0], cell[1])}
is calling FixNum#[] giving you the n-th bit of the integer. This was a bit of surprise to me too - it seems like a lot could go subtly wrong when accidentally supplying an integer instead of an Array.
With the original code, cell[0] and cell[1] evaluates as 3[0] and 3[1] for the first element of data. With the correction you have the array [3,2][0] => 3, and [3,2][1] => 2 which makes much more understandable code as a "collect" example.
1- collect is a iterator method that accepts a block of code.The collect iterator returns all the elements of a collection.
2- u haven't specified the value to be displayed. do "puts wheel.rim + (wheel.tire*2)".
3- if u print the 'wheel' in the collect block of diameters method, its
"#<struct RevealingReferences::Wheel rim=1, tire=1>"
"#<struct RevealingReferences::Wheel rim=0, tire=1>"
"#<struct RevealingReferences::Wheel rim=1, tire=0>"
"#<struct RevealingReferences::Wheel rim=0, tire=0>"
When the "wheel.rim + (wheel.tire*2)" statement is executed, the result is 3,2,1,0 and each result is returned. if the statement "puts wheel" is added in the collect block for diameter and the prog executed, u wont see the values (3,2,1,0) in the output.
I am new to Ruby and I am trying to write a program which convert Roman numerals to numbers.
This is what I did so far:
roman_numbers = {"M" => 1000, "D" => 500, "C" => 100, "L" => 50, "X" => 10, "V" => 5, "I" => 1}
number_by_user = "MCMXCIX"
singlenum = number_by_user.split(//).reverse!
l = singlenum.length
result =0
result = roman_numbers[singlenum[0]]
puts result
for i in 0..l-1
if roman_numbers.key?(singlenum[i])
**if (roman_numbers[singlenum[i]] > roman_numbers[singlenum[i+1]])** #gives error
result = result - roman_numbers[singlenum[i+1]]
elsif (roman_numbers[singlenum[i]]== roman_numbers[singlenum[i+1]] || **roman_numbers[singlenum[i]] < roman_numbers[singlenum[i+1]])** #gives error
result = result + roman_numbers[singlenum[i+1]]
end
puts roman_numbers[singlenum[i]]
else
puts "One of the values are not roman"
break
end
end
puts "The number is: " , result
but it gives me the following error (see the line with comment):
:in `>': comparison of Fixnum with nil failed (ArgumentError)
You have an off-by-one error. Arrays are 0 indexed. Your singlenum.length in this case is 7, but in your for loop, you go up to 6, then try to reference singlenum[7] to compare to singlenum[6]. singlenum[7] is nil, so it doesn't understand the < operator.
Marc did a good job of explaining where the error was in your code. However, such an error should never have occurred in the first place, since in Ruby, all collections already know how to iterate over themselves: you don't have to do that, so you can never even make such a mistake!
Here's an example of how one might implement the same algorithm in more idiomatic Ruby:
numerals = {
'M' => 1000,
'D' => 500,
'C' => 100,
'L' => 50,
'X' => 10,
'V' => 5,
'I' => 1
}
num = 'MCMXCIX'
(num.chars.map(&numerals.method(:[])) << 0).each_cons(2).inject(0) {|a, (n1, n2)|
if n1 < n2 then a - n1 else a + n1 end
}
See? No loops. No indices. You cannot make an off-by-one error even if you tried!
Bonus: actually, the above snippet does contain a piece of code to prevent an off-by-one error, albeit one at a much higher semantic level. Can you find it?
How can I increment an Integer variable by X without creating a new object instance?
+= does not work because:
ree-1.8.7-2010.02 > x = 1
1
ree-1.8.7-2010.02 > x.object_id
3
ree-1.8.7-2010.02 > x += 1
2
ree-1.8.7-2010.02 > x.object_id
5
You can't. Not in Ruby, and not in any other programming language I am aware of.
The object which represents the mathematical number 1 will always have the value 1. Mutating the object which represents the mathematical number 1 to suddenly have the value 2 would quite simply be insane, because now all of a sudden 1 + 1 == 4.
Extend your example for a moment. Try this:
x = 2
y = 1 + 1
x.object_id
y.object_id
Every unique number will have its own identity. Ruby's object orientedness goes a bit deeper than you will find with C++ and Java (both of those have the concept of primitives and classes).
What's important is that when you query x the second time for its value the value will be what you expect. Object identifiers don't really matter unless you are the garbage collector.
It gets worse with Bignums
begin
a = 1234567890
puts a.object_id
b = 1234567890
puts b.object_id
end
gave me
10605136
10604960
Execution time is really terrible even if just you organize a simple loop. Primitives shouldn't be ditched from Ruby.
(1..16000).each do
(1..16000).each do
end
end
This itself takes 30-40 seconds to complete (Lenovo T400, Virtualboxed Ubuntu), and you haven't even done something sophisticated.
You can use a helper class:
class Variable
def initialize value = nil
#value = value
end
attr_accessor :value
def method_missing *args, &blk
#value.send(*args, &blk)
end
def to_s
#value.to_s
end
# here's the increment/decrement part
def inc x = 1
#value += x
end
def dec x = 1
#value -= x
end
end
x = Variable.new 1
puts x #=> 1
puts x.object_id #=> 22456116 (or whatever)
x.inc
puts x #=> 2
puts x.object_id #=> 22456116
x.inc 3
puts x #=> 5
puts x.object_id #=> 22456116
More uses of "class Variable" here.