Type-Conversion in Ruby - ruby

Theoretical question
I'm trying to find new practical ways to convert integers into strings and the other way around.
I only know the .to_s ; .to_i ; .to_f methods and would like to know if there are other ways to do to it without writing + to put together the variables. For example:
var1 = 16
puts 'I\'m ' + var1.to_s + ' years old.'
In longer codes is getting tiring writing all this to just convert a integer to a string.
By the way I also found this Timer program here on Stack and the #{ is an example of what I'm trying to do. Adding an integer to a string without + and .to_s But I don't know how it works.
30.downto(0) do |i|
puts "00:00:#{'%02d' % i}"
sleep 1
end
Thank you in advance for the suggestions!

Ruby has a pretty powerful string interpolator feature using #{...} where that can contain fairly arbitrary Ruby code. The end result is always converted to a string using, effectively, to_s.
That is you can do this:
puts "00:00:#{'%02d' % i}"
Where that gets stringified and then interpolated.
This is roughly the same as:
i_str = '%02d' % i
puts "00:00:#{i_str}"
Where that is effectively:
i_str = '%02d' % i
puts "00:00:%s" % i_str
You could also combine that into a single operation:
puts "00:00:%02d" % i
Where you generally use interpolation or sprintf-style template strings, not both at the same time. It keeps your code cleaner since only one mechanism is in play.
The only reason .to_s is needed when doing concatenation is Ruby is very particular about "adding" together two things. x + y has a completely different outcome depending on what x and y are.
Consider:
# Integer + Integer (Integer#+)
1 + 2
# => 3
# Array + Array (Array#+)
[ 1 ] + [ 2 ]
# => [1,2]
# String + String (String#+)
"1" + "2"
# => "12"
Note that in each case it's actually a different method being called, and the general form of x + y is:
x.send(:+, y)
So it's actually a method call, and as such, each method may impose restrictions on what it can operate on by emitting exceptions if it can't or won't deal.

It's called string interpolation. For example:
puts "I\'m #{var1} years old."
The way it works is this:
You have to enclose the string in double quotes, not single quotes.
You put your variable inside this: #{}, e.g. "#{variable}".
This will always convert non-string variables into strings, and plug (i.e. interpolate) them into the surrounding string.

Related

Ruby equivalent of Python's conventional number separation

In Python, we have something like print(f'{int(input()):,}') that separates numbers by commas. That is for example, if we provide the input of 70671891032 we get 70,671,891,032. Is there anything similar to this in Ruby (without the use of regex)?
Ruby's built-in string formatting doesn't have an option for a thousands separator.
If you don't want to use regular expressions, you could insert a separator every 3 characters from the right, e.g. via step:
input = 70671891032
string = input.to_s
(string.size - 3).step(1, -3) do |i|
string.insert(i, ',')
end
string
#=> "70,671,891,032"
I expect this is not what you were looking for, but it does convert a non-negative integer to a string of digits with thousands separators.
def commafy(n)
s = ''
loop do
break s if n.zero?
n, n3 = n.divmod(1000)
s[0,0] = (n.zero? ? ("%d" % n3) : (",%03d" % n3))
end
end
input = 70671891032
commafy(input)
#=> "70,671,891,032"

Ruby syntax confusion brackets and hash

Objective: Use the .each method on the odds array to print out double the value of each item of the array. In other words, multiply each item by 2.
Make sure to use print rather than puts, so your output appears on one line.
code:
odds = [1,3,5,7,9]
odds.each do |x|
x *= 2
print "#{x}"
end
Doing this exercise on Codecademy, I'm rather confused about the syntax as why there needs to be a #{} surrounding the x and why can't it just be: print "x" or print #x". What are the roles of the hash and brackets? It perplexes me as why ruby doesn't print out "#{X}" rather than the "x" number multiplied by 2 due to it being surrounded by quotation marks? Previous exercises also featured both hashes and curly brackets #{user_input} where the console printed whatever we typed rather than print out "#{user input}" itself.
You can't do print "x" and expect it prints the value of the x object. What if you wanted to print the x letter? So, we need interpolation for that.
But interpolation used in that way has just the collateral effect to make xa string. You can do the same without interpolation:
print x
Anyway, I'd probably do
odds = [1,3,5,7,9]
odds.each { |x| print x * 2 }
About interpolation: you can use it when you should concatenate several strings and it would be ugly. For instance
puts name + " " + surname
puts "#{name} #{surname}"
I prefer the latter

How to implement addition operator in math parser (ruby)

I'm trying to build my own evaluator for mathematical expressions in ruby, and before doing that am trying to implement a parser to break the expression into a tree(of arrays). It correctly breaks down expressions with parenthesis, but I am having lots of trouble trying to figure out how to make it correctly break up an expression with operator precedence for addition.
Right now, a string like 1+2*3+4 becomes 1+[2*[3+4]] instead of 1+[2*3]+4. I'm trying to do the simplest solution possible.
Here is my code:
#d = 0
#error = false
#manipulate an array by reference
def calc_expr expr, array
until #d == expr.length
c = expr[#d]
case c
when "("
#d += 1
array.push calc_expr(expr, Array.new)
when ")"
#d += 1
return array
when /[\*\/]/
#d +=1
array.push c
when /[\+\-]/
#d+=1
array.push c
when /[0-9]/
x = 0
matched = false
expr[#d]
until matched == true
y = expr.match(/[0-9]+/,#d).to_s
case expr[#d+x]
when /[0-9]/
x+=1
else matched = true
end
end
array.push expr[#d,x].to_i
#d +=(x)
else
unless #error
#error = true
puts "Problem evaluating expression at index:#{#d}"
puts "Char '#{expr[#d]}' not recognized"
end
return
end
end
return array
end
#expression = ("(34+45)+(34+67)").gsub(" ","")
evaluated = calc #expression
puts evaluated.inspect
For fun, here's a fun regex-based 'parser' that uses the nice "inside-out" approach suggested by #DavidLjungMadison. It performs simple "a*b" multiplication and division first, followed by "a+b" addition and subtraction, and then unwraps any number left in parenthesis (a), and then starts over.
For simplicity in the regex I've only chosen to support integers; expanding each -?\d+ to something more robust, and replacing the .to_i with .to_f would allow it to work with floating point values.
module Math
def self.eval( expr )
expr = expr.dup
go = true
while go
go = false
go = true while expr.sub!(/(-?\d+)\s*([*\/])\s*(-?\d+)/) do
m,op,n = $1.to_i, $2, $3.to_i
op=="*" ? m*n : m/n
end
go = true while expr.sub!(/(-?\d+)\s*([+-])\s*(-?\d+)/) do
a,op,b = $1.to_i, $2, $3.to_i
op=="+" ? a+b : a-b
end
go = true while expr.gsub!(/\(\s*(-?\d+)\s*\)/,'\1')
end
expr.to_i
end
end
And here's a bit of testing for it:
tests = {
"1" => 1,
"1+1" => 2,
"1 + 1" => 2,
"1 - 1" => 0,
"-1" => -1,
"1 + -1" => 0,
"1 - -1" => 2,
"2*3+1" => 7,
"1+2*3" => 7,
"(1+2)*3" => 9,
"(2+(3-4) *3 ) * -6 * ( 3--4)" => 42,
"4*6/3*2" => 16
}
tests.each do |expr,expected|
actual = Math.eval expr
puts [expr.inspect,'=>',actual,'instead of',expected].join(' ') unless actual == expected
end
Note that I use sub! instead of gsub! on the operators in order to survive the last test case. If I had used gsub! then "4*6/3*2" would first be turned into "24/6" and thus result in 4, instead of the correct expansion "24/3*2" → "8*2" → 16.
If you really need to do the expression parsing yourself, then you should search for both sides of an expression (such as '2*3') and replace that with either your answer (if you are trying to calculate the answer) or an expression object (such as your tree of arrays, if you want to keep the structure of the expressions and evaluate later). If you do this in the order of precedence, then precedence will be preserved.
As a simplified example, your expression parser should:
Repeatedly search for all inner parens: /(([^)+]))/ and replace that with a call to the expression parser of $1 (sorry about the ugly regexp :)
Now all parens are gone, so you are looking at math operations between numbers and/or expression objects - treat them the same
Search for multiplication: /(expr|number)*(expr|number)/
Replace this with either the answer or encapsulate the two expressions in
a new expression. Again, depending on whether you need the answer now or
if you need the expression tree.
Search for addition: ... etc ...
If you are calculating the answer now then this is easy, each call to the expression parser eventually (after necessary recursion) returns a number which you can just replace the original expression with. It's a different story if you want to build the expression tree, and how you deal with a mixture of strings and expression objects so you can run a regexp on it is up to you, you could encode a pointer to the expression object in the string or else replace the entire string at the outside with an array of objects and use something similar to regexp to search the array.
You should also consider dealing with unary operators: "3*+3"
(It might simplify things if the very first step you take is to convert all numbers to a simple expression object just containing the number, you might be able to deal with unary operators here, but that can involve tricky situations like "-3++1")
Or just find an expression parsing library as suggested. :)

How do I increment/decrement a character in Ruby for all possible values?

I have a string that is one character long and can be any possible character value:
irb(main):001:0> "\x0"
=> "\u0000"
I thought this might work:
irb(main):002:0> "\x0" += 1
SyntaxError: (irb):2: syntax error, unexpected tOP_ASGN, expecting $end
"\x0" += 1
^ from /opt/rh/ruby193/root/usr/bin/irb:12:in `<main>'
But, as you can see, it didn't. How can I increment/decrement my character?
Edit:
Ruby doesn't seem to be set up to do this. Maybe I'm approaching this the wrong way. I want to manipulate raw data in terms of 8-bit chunks. How can I best accomplish that sort of operation?
Depending on what the possible values are, you can use String#next:
"\x0".next
# => "\u0001"
Or, to update an existing value:
c = "\x0"
c.next!
This may well not be what you want:
"z".next
# => "aa"
The simplest way I can think of to increment a character's underlying codepoint is this:
c = 'z'
c = c.ord.next.chr
# => "{"
Decrementing is slightly more complicated:
c = (c.ord - 1).chr
# => "z"
In both cases there's the assumption that you won't step outside of 0..255; you may need to add checks for that.
You cannot do:
"\x0" += 1
Because, in Ruby, that is short for:
"\x0" = "\x0" + 1
and it is a syntax error to assign a value to a string literal.
However, given an integer n, you can convert it to a character by using pack. For example,
[97].pack 'U' # => "a"
Similarly, you can convert a character into an integer by using ord. For example:
[300].pack('U').ord # => 300
With these methods, you can easily write your own increment function, as follows:
def step(c, delta=1)
[c.ord + delta].pack 'U'
end
def increment(c)
step c, 1
end
def decrement(c)
step c, -1
end
If you just want to manipulate bytes, you can use String#bytes, which will give you an array of integers to play with. You can use Array#pack to convert those bytes back to a String. (Refer to documentation for encoding options.)
You could use the String#next method.
I think the most elegant method (for alphanumeric chars) would be:
"a".tr('0-9a-z','1-9a-z0')
which would loop the a through to z and through the numbers and back to a.
I reread the question and see, that my answer has nothing to do with the question. I have no answer for manipulationg 8-bit values directly.

Adding a number to a phrase

I can print a raw number with this code:
puts 'Please enter your favorite number'
favNumber = gets.chomp
betterNumber = favNumber.to_i
puts betterNumber + 1
but I need to set a message including the number. I changed the last two lines to this, but it's wrong.
betterNumber = favNumber.to_i + 1
puts 'Your favorite number sucks, a better number is '+ betterNumber + '!'
Help me.
betterNumber is of class Fixnum and your string is of course of class String. You can't add a String and a Fixnum, you need to cast your Fixnum into a String using to_s.
"Your favorite number sucks, a better number is " + betterNumber.to_s + "!"
Also, using interpolation calls to_s on any objects being interpolated. So this works, too (and is more common):
"Your favorite number sucks, a better number is #{betterNumber}!"
Also, in Ruby we usually use snake_case variables as opposed to camelCase variables. So I recommend using better_number
You need to convert betterNumber to a string when printing it, like this: betterNumber.to_s.

Resources