problem = 7 + 3
puts problem.to_s
I'm new to Ruby. The code above returns 10. How do I get 7 + 3 as the output, without assigning it to problem as a string in the first place? Am I missing something extremely simple?
Am I missing something extremely simple?
Yes, you are. This is impossible. 7 + 3, as an arbitrary expression, is evaluated/"reduced" to 10 when it's assigned to problem. And the original sequence of calculations is lost.
So you either have to start with strings ("7 + 3") and eval them when you need the numeric result (as #zswqa suggested).
Or package them as procs and use something like method_source.
just for fun (don't try at home)
class Integer
def +(other)
"#{self} + #{other}"
end
end
problem = 7 + 3
puts problem.to_s # "7 + 3"
7 is Fixnum (ruby < 2.4) or Integer (ruby >= 2.4)
"7" is String
So you need to define for ruby what you need because + for Integer is addition, + for String is concatenation:
problem = "7" "+" "3"
That is equal to:
problem = "7+3"
Related
For example:
def recurse(value)
if value < 5
self.send(__method__, value + 1)
else
value
end
end
This works, but it's a bit ugly.
Basically I'm looking for a prettier way to call the currently executing method, without referring to it explicitly by name.
If there is a less-cryptic syntax for this, I would probably use it (to avoid the name duplication, reduce effort required for renaming a function, etc). If there isn't a nicer syntax for this, I'll just hard-code the name like normal.
It's a comment rather, as #sagarpandya82 mentioned, you can omit some redundant parts and use both variants. I would refactor it a bit:
def recurse(value)
return value unless value < 5 # return value if value >= 5
send(__method__, value + 1) # or just recurse(value + 1)
end
Non-recursion version with a block:
def non_recurse(value)
if value >= 5
yield value
else
(value..5).each do |i|
yield i
end
end
end
non_recurse(3) {|i| puts i}
#=> 3, 4, 5
non_recurse(6) {|i| puts i}
#=> 6
If you really want to use __method__, your method is correct and reasonably readable. To comply with usual Ruby guidelines, you could just remove returns and use 2 spaces as indent (as mentioned by #sagarpandya82 in the comments):
def recurse(value)
if value < 5
self.send(__method__, value + 1)
else
value
end
end
I don't see any reason to use self.send(__method__) here, so you could write :
def recurse(value)
if value < 5
recurse(value + 1)
else
value
end
end
Actually, I'd say that you don't need recursion at all. All your method does is to keep adding 1 to the value until it reaches 5. If the value is bigger than 5, it returns the value :
For integers:
def no_recurse(value)
[value, 5].max
end
no_recurse(4)
# 5
no_recurse(-3)
# 5
no_recurse(7)
# 7
no_recurse(-2**1000)
# 5
no_recurse(4.5)
# 5 # <- That's wrong
For floats, you'd just need to add the decimal part to 5. This will work for any number:
def no_recurse(value)
[value, 5 + value % 1].max
end
no_recurse(4.5)
# 5.5
no_recurse(5.5)
# 5.5
no_recurse(6)
# 6
no_recurse(-7)
# 5
In my Ruby app, I have an Investment entity that has a term attribute.
I need this class to accept a String from user input in the form of 3 Years or 36 months. What I want is to then convert the input into number of months, set the term attribute to this period and figure out the maturity date.
So far I have tried using Active Support and Chronic but the APIs do not support this.
This getter works:
def term
if term =~ /year[s]?/i
term = term.to_i * 12
else term =~ /month[s]?/i
term = term.to_i
end
end
But is there a more elegant way to do this in Ruby?
Ruby doesn't have anything built-in that represents a "time-span" (some other languages do). However, there is a library for it (timespan), although it may be a bit overkill for your situation.
You mentioned that chronic doesn't support this. But why not just calculate the time difference yourself?
require 'chronic'
input = '2 years'
then = Chronic.parse(input + ' ago')
now = Time.now
# Now we just calculate the number of months
term = (now.year * 12 + now.month) - (then.year * 12 + then.month)
That way, you get the flexibility of chronic's parsing, and you still don't need much code.
Or just go ahead and use the timespan library.
require 'timespan'
term = Timespan.new('2 years').to_months
# Boom.
If we can assume that the input string will always contain one or more digits followed a unit ("years", "Year", "months", etc.), this is pretty straightforward. Just write a regular expression that captures the digits and the unit, convert the digits to a number and normalize the unit, and do the math.
def to_months(str)
if str =~ /(\d+)\s*(month|year)s?/i
num = $1.to_i # => 3
unit = $2.downcase # => year
num *= 12 if unit == "year"
return num
end
raise ArgumentError, "Invalid input"
end
puts to_months("3 Years") # => 36
puts to_months("1 month") # => 1
puts to_months("6months") # => 6
It's not a whole lot more elegant than your method, but perhaps it'll give you an idea or two.
This question already has answers here:
ruby basic data type conversion
(4 answers)
Closed 1 year ago.
I'm solving this ruby kata. Essentially what the code does is to output an integer with five digits.
Example:
5 = 00005
12 = 00012
12345 = 12345
00001234 = 012345
Here is my code:
def solution(value) #00001204
string_value = value.to_s
if string_value.length <= 5
amount_of_zeros = "0" * (string_value.length - 5).abs
puts "Value is #{amount_of_zeros}" + "#{string_value}"
else
start_of_characters = 5 - string_value.length #-3
puts "Value is " + string_value[-1..start_of_characters]
end
end
Everything works fine until I place 00001204. For some reason I get the output 00644. I tried using binding.pry to see what was going on and my number gets converted into 644 from the start. Why is it doing that? The docs don't mention anything about it. I don't how to fix this because on the first line of my method it already turns into 644. Any thoughts?
In ruby, numbers that are 0-prefixed are interpreted as octal. When you pass 00001204 to your method, ruby is assuming that you want the number interpreted as octal. 12048 = 64410.
644.to_s 8
=> "1204"
01204.to_s 10
=> "644"
Check out the Ruby documentation on literals.
as Zajn said it is due to it being interpreted as octal.
You can also use .to_i to force it to be an integer.
>> 000000001204
=> 644
>> "000000001204".to_i
=> 1204
Also take a look at string formatting with % if you just want to output it, since it can clean up your code a lot
>> "%05d" % "000001204".to_i
=> "01204"
def sumof
s = 12 + 17
puts "The sum of 12 and 17 is: " + s
end
When I call sumof, I'm getting an error
Thanks for helping
There is really one way to convert to a string, but it can be used in multiple ways. The method is called to_s (to string).
Way 1 (manual):
"Some string " + num.to_s
Way 2 (interpolation):
"Some string #{num}"
You're going to want to do
"The sum is " + s.to_s OR "The sum is #{s}"
the problem being that conversion to string is not implicitly done in your original example.
I need a special behavior from eval, to evaluate strings like:
'5a + 6b + 3a + 11b'
into
'8a + 17b'
Is it possible? If it is, can you give a proper example? I found this example recently, where the talk was about evaluating strings with meters and inches.
The way it is written, that's not valid ruby syntax, so it can't be evaled.
You have two options:
a) Don't use eval. I would think that this is the best option, but I supposed that depends on the context.
b) Change the syntax to 5.a + 6.b + 3.a + 11.b and define appropriate a and b methods, like so:
class Expr
attr_accessor :a,:b
def initialize(a,b)
#a, #b = a,b
end
def +(other)
Expr.new(a + other.a, b + other.b)
end
def inspect
"<expr: #{a}a + #{b}b>"
end
end
class Numeric
def a
Expr.new(self, 0)
end
def b
Expr.new(0, self)
end
end
5.a + 6.b + 3.a + 11.b #=> <expr: 8a + 17b>
Well, the problem here is not related to Ruby's eval. You need a simple interpreter (parser+evaluator) to interpret the above mentioned expressions. There are some solutions for your problem but you need to give a proper explanation for the syntax you are trying to parse.
For example in your case I would do something like this:
res = ['a','b'].map do |sym|
'5a + 6b + 3a + 11b'.scan(/(\d+)#{sym}/).flatten.inject(0){|sum,x| sum + x.to_i}.to_s + sym
end.join(" + ")
puts res #=> 8a + 17b
I guess depending on your problem this might be very complex. I do not know a library for mathematical optimization in ruby. I fear it is not possible with ruby eval. But you probably could write your own optimizer. If you think of the expression that way:
+
*
5
a
*
6
b
*
3
a
*
11
b
You can group summands with equal variable, summarize the numbers and create a new expression string. For the parsing Treetop could be worth a look.