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.
Related
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"
Can Anyone point Me in the direction of how to tell when, for example, the + operator is used in ruby as opposed to the += operator from the inside of the + operator's definition? To illustrate:
class A
def +(b)
if is_theCallActuallyACompoundAssignment?
compoundAssignment = true
else
compoundAssignment = false
end
doOtherStuff
end
end
Is there a Kernel method, perhaps?
This code:
a += 5
Gets translated to this:
a = a + 5
Your + method will not know that you received a compound assignment.
When programming ruby I always find myself doing this:
a = [a, b].min
This means compare a and b and store the smallest value in a. I don't like writing the code above as I have to write a twice.
I know that some non-standard dialects of C++ had an operator which did exactly this
a <?= b
Which I find very convenient. But I'm not really interested in the operator as much as I'm in the feature of avoiding repetition. I would also be happy if I could write
a.keep_max(b)
a can be a quite long variable, like my_array[indice1][indice2], and you don't want to write that twice.
I did alot of googling on this and found no result, hopefully this question will pop up and be useful for others aswell.
So, is there any non-repeitive way to express what I want in ruby?
What you would like to do is in fact not possible in ruby (see this question). I think the best you can do is
def max(*args)
args.max
end
a = max a, b
I don't understand your question. You can always do something like this ...
module Comparable
def keep_min(other)
(self <=> other) <= 0 ? self : other
end
def keep_max(other)
(self <=> other) >= 0 ? self : other
end
end
1.keep_min(2)
=> 1
1.keep_max(2)
=> 2
Well, that won't work for all objects with <=> because not all of them are implementing Comparable, so you could monkey-patch Object.
Personally I prefer clarity and tend to avoid monkey-patching. Plus, this clearly is a binary predicate, just like "+", therefore method-chaining doesn't necessarily make sense so I prefer something like this to get rid of that array syntax:
def min(*args)
args.min
end
def max(*args)
args.max
end
min(1, 2)
=> 1
max(1, 2)
=> 2
But hey, I'm also a Python developer :-)
You can define your own method for it:
class Object
def keep_max(other)
[self, other].max
end
end
a = 3
b = 7
puts a.keep_max(b)
But you should be careful defining methods on Object as it can have unpredictable behaviour (for example, if objects cannot be compared).
def keep_max(var, other, binding)
eval "#{var} = [#{var}, #{other}].max", binding
end
a = 5
b = 78
keep_max(:a, :b, binding)
puts a
#=> 78
This basically does what you want. Take a look at Change variable passed in a method
i've been messing around with ruby and opengl for entertainment purposes, and i decided to write some 3d vector/plane/etc classes to pretty up some of the math.
simplified example:
class Vec3
attr_accessor :x,:y,:z
def *(a)
if a.is_a?(Numeric) #multiply by scalar
return Vec3.new(#x*a, #y*a, #z*a)
elsif a.is_a?(Vec3) #dot product
return #x*a.x + #y*a.y + #z*a.z
end
end
end
v1 = Vec3.new(1,1,1)
v2 = v1*5 #produces [5,5,5]
which all fine and dandy, but i also want to be able to write
v2 = 5*v1
which requires adding functionality to Fixnum or Float or whatever, but i couldn't find a way to overload or extend fixnum's multiplication without replacing it entirely. is this possible in ruby? any tips?
(obviously i can just write all my multiplications in the correct order if i need to)
Using coerce is a MUCH better approach than monkey-patching a core class:
class Vec3
attr_accessor :x,:y,:z
def *(a)
if a.is_a?(Numeric) #multiply by scalar
return Vec3.new(#x*a, #y*a, #z*a)
elsif a.is_a?(Vec3) #dot product
return #x*a.x + #y*a.y + #z*a.z
end
end
def coerce(other)
return self, other
end
end
if you define v as v = Vec3.new then the following will work: v * 5 and 5 * v
The first element returned by coerce (self) becomes the new receiver for the operation, and the second element (other) becomes the parameter, so 5 * v is exactly equivalent to v * 5
I believe the following will do what you want, though banister's suggestion to use coerce instead of monkey-patching Numeric is a preferred method. Use this method only if necessary (for example if you only want some binary operands to be transitive).
Fixnum.class_eval do
original_times = instance_method(:*)
define_method(:*) do |other|
if other.kind_of?(Vec3)
return other * self
else
return original_times.bind(self).call(other)
end
end
end
I've seen how to overload + and * in Ruby, so that
my_obj + other calls my_obj.+(other). In Python, you do this with __add__, and there's a corresponding __radd__ for overloading other + my_obj. Is there really no equivalent right-sided addition/multiplication in Ruby, and does that make it necessary to redefine + for each potential class of other?
In brief: say I have an object X which belongs to some new class defined by me. It's easy to write code for X + 5, but it seems that in order to handle 5 + X I'd need to redefine Fixnum.+. Is this true?
No, you don't need to redefine Fixnum#+ (or any other arithmetic method of ruby's numeric classes). The arithmetic methods of the numeric classes will call coerce if the two operands are not the same class. So if you define a coerce method for your class, 5 + instance_of_your_class will work fine without any changes to Fixnum#+.
Edit: Here's an example of using coerce:
class MyNum
attr_accessor :num
def initialize(num)
#num = num
end
def +(o)
lhs, rhs = coerce(o)
MyNum.new(lhs.num + rhs.num)
end
def coerce(o)
if o.is_a? MyNum
[self, o]
else
[self, MyNum.new(o)]
end
end
end
MyNum.new(5)+3 #=> #<MyNum:0x87fca08 #num=8>
3+MyNum.new(5) #=> #<MyNum:0x8807980 #num=8>
The RHS is coerced and needs to implement the to_i method which needs to return an appropriate value. The Numeric's classes implementation of to_int will end up calling it as part of the process of addition where other classes are involved on the RHS than strict Numeric descendants.
Yes. If you want to alter the behavior on 5 + x, you have to redefine + on whatever 5 is, since you're in fact calling 5.+(x) because of Ruby treating these kinds of operations as mechod calls.