Convert Integer to Octal using class Integer and multiple methods - ruby

I have a script started but am receiving an error message. I typically have the right idea, but incorrect syntax or formatting.
Here are the exact instructions given:
Extend the Integer class by adding a method called to_oct which returns a string representing the integer in octal. We'll discuss the algorithm in class. Prompt the user for a number and output the octal string returned by to_oct.
Add another method to your Integer extension named "to_base". This method should take a parameter indicating the base that the number should be converted to. For example, to convert the number 5 to binary, I would call 5.to_base(2). This would return "101". Assume that the input parameter for to_base is an integer less than 10. to_base should return a string representing the decimal in the requested number base.
#!/usr/bin/ruby
class Integer
def to_base(b)
string=""
while n > 0
string=(n%b)+string
n = n/b
end
end
def to_oct
n.to_base(8)
end
end
puts "Enter a number: "
n=gets.chomp
puts n.to_base(2)
When I run the script I do get the enter a number prompt, but then I get this error message:
tryagain.rb:16:in `<main>': undefined method `to_base' for "5":String (NoMethodError)

As suggested, do something like this:
class Integer
def to_base b
to_s b #same as self.to_s(b)
end
def to_oct
to_base 8 #same as self.to_base(8)
end
end
5.to_base 2 #=> "101"
65.to_oct #=> "101"

Related

rand(Range) - no implicit conversion of Range into Integer

A follow up on the question How to create a random time between a range
.
Kernel#rand works with Time range:
require 'time'
rand(Time.parse('9 am')..Time.parse('11:30 am'))
But when I tried with a custom class, I ended up with the error:
`rand': no implicit conversion of Range into Integer (TypeError)
class Int
include Comparable
attr_reader :num
def initialize(num)
#num = num
end
def succ
Int.new(num + 1)
end
def <=>(other)
num <=> other.num
end
def to_s
"Int(#{num})"
end
def to_int
#num
end
alias_method :inspect, :to_s
end
puts rand(Int.new(1)..Int.new(3))
Why? What am I missing in the custom class? Can we use such a custom class in rand(Range)?
I don't know of any documentation for what specifically Kernel#rand expects from a Range argument but we can get a look at what's going on by overriding respond_to? in your class and then watching as things fall apart:
def respond_to?(m)
puts "They want us to support #{m}"
super
end
Doing that tells us that rand wants to call the #- and #+ methods on your Int instances. This does make some sense given that rand(a..b) is designed for working with integers.
So we throw in quick'n'dirty implementations of addition and subtraction:
def -(other)
self.class.new(to_int - other.to_int)
end
def +(other)
self.class.new(to_int + other.to_int)
end
and we start getting rand Ints out of our calls to rand.
I'm not sure where (or if) this is documented so you'll have to excuse a bit of hand waving. I normally spend some time rooting around the Ruby source code to answer this sort of question but I lack the time right now.
To add a bit more to #mu-is-too-short's answer, I checked the source of Random#rand and the following is the current implementation logic for rand(Range):
Get the begin, end, and vmax from the Range object (call range_values), where vmax is computed as (call id_minus):
vmax = end - begin
vmax will be used as the upper bound of the random number generation later.
This requires the custom class to have - method defined.
Generate a random number based on the type of vmax:
If it is not Float and can be coerced to Integer (rb_check_to_int), generate a random Integer less than vmax.
In this case, the - method should either return an Integer, or an object which responds to to_int method.
If it is Numeric and can be converted to Float with to_f, (rb_check_to_float), generate a random Float number less than vmax.
In this case, the - method should return a Numeric number which can be converted to Float with method to_f.
Add the random number to begin to yield the result (call id_add).
This requires the custom class to have + method defined, which accepts the result of the random number generated in step 2 (either Integer, or Float) and returns the final result for rand.
I believe this error is because you are trying to use rand() on objects of your custom class.
`rand': no implicit conversion of Range into Integer (TypeError)
This error message clearly mentions that ruby was unable to convert your range into integer. Based on your code snippet, following works and might be what you are looking for.
puts rand(Int.new(1).to_int..Int.new(3).to_int)

Ruby: undefined method `digits' for 3212:Fixnum (NoMethodError)

The code below is meant to take in an integer, square each digit, and return the integer with the squared digits.
However, I kept having this error:
`square_digits': undefined method `digits' for 3212:Fixnum (NoMethodError)
from `
'
I don't understand why I have this error as the .digits method is an included method in ruby and I'm using it on an integer, yet it gives me a NoMethodError.
def square_digits(digit)
puts digit
puts digit.inspect
puts digit.class
if digit <= 0
return 0
else
#converts the digit into digits in array
split_digit = digit.digits
puts "split digit class and inspect"
puts split_digit.inspect
puts split_digit.class
# multiples each digit by itself
squared = split_digit.map{ |a| a * a}
squared.reverse!
# makes digits into string
string = squared.join('')
# converts string into integer
string.to_i
end
end
Does anyone know what is going on??
I guess you are using older version of Ruby than 2.4.0. If so then this method will not be available. It is added in 2.4.0. See this link
To add support for you old ruby version you can just add below code before your method definition.
class Integer
def digits(base: 10)
quotient, remainder = divmod(base)
quotient == 0 ? [remainder] : [*quotient.digits(base: base), remainder]
end
end
After adding this code snippet you should be able to run your method.

Ruby to_s conversion to binary argument error

I was trying to solve a HackerRank problem which requires binary manipulation. The test cases are extremely huge numbers so I thought better to manipulate them as strings.
t=gets
def winner(pturn)
if a%2==0
puts "Richard\n"
else
puts "Louise\n"
end
end
while t != 0
turn=1
n=gets
(n2=n).to_s(2)
while n!=1
one="1"
zero="0"
if n2.count(1)>1
zero*=(n2.length - 2)
one.concat(zero)
n-=one.to_i(base=2)
else
n/=2
end
turn+=1
end
winner(turn)
t-=1
end
It caused an argument error (wrong number of argument) in line as seen below.
(n2=n).to_s(2)
I think I'm using to_s wrongly. I can't see the mistake and need someone to point it out.
I think you are trying to convert a number in Ruby to a string with it's binary representation. This is possible with (if the number is actually a number in Ruby, e.g. Fixnum) :
4.to_s 2
# => "100"
But in your case, what you are getting after calling gets is a string, and the to_s method of the String class simply returns itself, and doesn't take any arguments, hence the error.
You can fix it by calling gets.to_i instead of just gets, so the read string of contents will be converted to an integer in Ruby (you should be sure that you will only be reading numbers at there).
And I believe you are trying to assign the binary representation (as a string) of n into the variable n2, for that you should be doing
n2 = n.to_s(2)
If you just do :
(n2=n).to_s
Because of the parenthesis, first n2 will be assigned the value of n, then to_s will be called on n2, the string version of n2 will be returned, and nothing else happens. First thing you should be doing is the conversion, then the assignment.
And also you should pass in a string while calling String#count, i.e. you should call n2.count('2') instead of n2.count(2).
Argument error is coming because you are trying to pass parameter to to_s method because to_s method never accept any arguments.
So you should change (n2=n).to_s(2) to
(n2=n).to_s

Ruby: Take input and take it as loop range

a = $stdin.read
for i in 0..(a)
puts "Hi"
end
This is giving syntax error-in `
': bad value for range (ArgumentError). What should be improved to get output for a=3 as
Hi
Hi
Hi
The error is because a is a string, you can make it an integer by:
a = a.to_i
You have to convert the string to an integer (.to_i).
Note: prefer to use times:
a.to_i.times { puts "Hi" }
You need an integer to be used, or you get the ArgumentError. This will accept your input and ensure that it can be converted to an Integer. You can read about the Kernel#Integer method for specifics.
a = Integer($stdin.read)
for i in 0..(a)
puts "Hi"
end

Trying to convert a number to binary form

I wrote a simple program which works:
a = 4.to_s(2)
puts a.reverse
I want to be able to change it based on input from the user in the terminal. This is what I wrote:
puts 'Please enter a number you would like to see expressed in binary form'
i = gets.chomp
b = i.to_s(2)
puts b
This is the error that I keep getting:
`to_s': wrong number of arguments(1 for 0) (ArgumentError)
You're starting with a string, so you need to convert it:
i.to_i.to_s(2)
The #to_s method on a string doesn't take any arguments.
You do not need the chomp method, #to_i will take care of it.
Write it as:
puts 'Please enter a number you would like to see expressed in binary form'
i = gets.to_i
b = i.to_s(2)
puts b
You are calling to_s on an string, not on integer as you are thinking, because Kernel#gets always gives you a String object.
First, convert it to Fixnum, then call Fixnum#to_s on the Fixnum instance, which takes an argument, but String#to_s doesn't accept arguments, which was why you got a complaint from Ruby.
It seems that you want to use Fixnum#to_s but in your program gets.chomp returns a string, so you actually call String#to_s
You can could do:
i = gets.chomp.to_i

Resources