Lexical Casts with Ruby

  • Thread starter Rüdiger Sonderfeld
  • Start date
R

Rüdiger Sonderfeld

Hello,
I want to assign some values I recive in string form to attributes of a
user specified class. The problem is that I have to lexical cast the
strings into the required type. I can't use to_i or to_f because the attribute
type can only be determined at runtime.

Is there a way for lexical casts between types in Ruby? Something like
convert("10", Fixnum)
=> 10

Regards,
Rüdiger Sonderfeld <[email protected]>
 
R

Robert Klemme

2005/9/3 said:
Hello,
I want to assign some values I recive in string form to attributes of a
user specified class. The problem is that I have to lexical cast the
strings into the required type. I can't use to_i or to_f because the attr= ibute
type can only be determined at runtime.
=20
Is there a way for lexical casts between types in Ruby? Something like
convert("10", Fixnum)
=3D> 10

Well, there are several options:

You can use send, like your_string.send:)to_i) where you determine the
symbol :)to_i, :to_f etc) at runtime.

You can use case

result =3D case your_string
when /^\d+$/ then your_string.to_i
when /^\d.\.\d+/ then your_string.to_f
else your_string
end

Or you put conversions into a map

conv =3D {
Fixnum =3D> lambda {|x| x.to_i},
Float =3D> lambda {|x| x.to_f},
}

result =3D conv[Fixnum][your_string]

Hope, that gets you started.

Kind regards

robert
 
D

David A. Black

--8323328-1020658632-1125761734=:19128
Content-Type: MULTIPART/MIXED; BOUNDARY="8323328-1020658632-1125761734=:19128"

This message is in MIME format. The first part should be readable text,
while the remaining parts are likely unreadable without MIME-aware tools.

--8323328-1020658632-1125761734=:19128
Content-Type: TEXT/PLAIN; charset=X-UNKNOWN; format=flowed
Content-Transfer-Encoding: QUOTED-PRINTABLE

Hi --

Hello,
I want to assign some values I recive in string form to attributes of a
user specified class. The problem is that I have to lexical cast the
strings into the required type. I can't use to_i or to_f because the attr= ibute
type can only be determined at runtime.

Is there a way for lexical casts between types in Ruby? Something like
convert("10", Fixnum)
=3D> 10

See Robert's answer. Also, another possibility would be to put the
knowledge directly in the object:

class MyClass
def x=3D(s)
@x =3D s.to_i
end

def y=3D(s)
@y =3D s.to_f
end

# ...
end

That way, you can just do:

obj.x =3D "10"

and have the conversion be encapsulated in the object.

If using the assignment syntax seems *too* transparent (since you're
not really setting it to "10"), you could use differently-named
methods. But the principle would be the same: put the knowledge in
the object.


David

--=20
David A. Black
(e-mail address removed)
--8323328-1020658632-1125761734=:19128--
--8323328-1020658632-1125761734=:19128--
 
P

Paul Brannan

Hello,
I want to assign some values I recive in string form to attributes of a
user specified class. The problem is that I have to lexical cast the
strings into the required type. I can't use to_i or to_f because the attribute
type can only be determined at runtime.

Is there a way for lexical casts between types in Ruby? Something like
convert("10", Fixnum)
=> 10

In C++, boost::lexical_cast uses streams to convert the object to a
string and then from the string to the desired type.

It is easy to do the first part in ruby; all objects have a to_s method.

The second part is harder. There is no uniform mechanism for converting
a string into an object.

Paul
 
E

Eric Mahurin

--- Paul Brannan said:
=20
In C++, boost::lexical_cast uses streams to convert the
object to a
string and then from the string to the desired type.
=20
It is easy to do the first part in ruby; all objects have a
to_s method.
=20
The second part is harder. There is no uniform mechanism for
converting
a string into an object.


A while back, I suggested adding some klass.from_* methods.=20
For example:

def Integer.from_s(s)
s.to_i;
end

Any class that could make one of its objects from a String
would put this method in. You could do the same for other
classes to convert from also: from_i, from_f, etc. Here would
be the usage:

Integer.from_s("10") # 10
10.to_s # "10" - nothing new

As a convienence you could also add this:

class String
def to(klass)
klass.from_s(self)
end
def self.from(obj)
obj.to_s
end
end

so you could do:

"10".to(Integer) # 10
String.from(10) # "10"

If this is a good solution for you, maybe an RCR for this more
general conversion mechanism is in order.



=09
=09
______________________________________________________
Click here to donate to the Hurricane Katrina relief effort.
http://store.yahoo.com/redcross-donate3/
 
P

Paul Brannan

so you could do:

"10".to(Integer) # 10
String.from(10) # "10"

If this is a good solution for you, maybe an RCR for this more
general conversion mechanism is in order.

See RCR#280. The implementation is 34 lines long.

Paul
 
E

Eric Mahurin

--- Paul Brannan said:
=20
See RCR#280. The implementation is 34 lines long.

Don't know why I missed that one. I don't really like the
implementation with the global (or rather class variable) hash.
I would rather see an encapsulated API - each class gives
methods for converting to/from objects of other classes of
interest (i.e. String). You may have duplicated code or one
from_* calling another to_* (or vice-versa), but so what. This
way requires 0 lines of overhead - nothing needs to manage
framework. It's simple and is an extension of what we already
have. The klass#to(toKlass) and klass.from(fromObj) are
shortcuts to get a little more abstract if you want:

class String
def self.from(obj); obj.to_s; end
def to(klass); klass.from_s(self); end
end

If you did this, then I guess you could consider this the
overhead for each class that other classes want to convert
to/from - String, Integer, Float, Array, etc.

I would think this more straight-forward approach would be more
likely accepted.



=09
=09
______________________________________________________
Click here to donate to the Hurricane Katrina relief effort.
http://store.yahoo.com/redcross-donate3/
 
G

gabriele renzi

Eric Mahurin ha scritto:
Don't know why I missed that one. I don't really like the
implementation with the global (or rather class variable) hash.
I would rather see an encapsulated API - each class gives
methods for converting to/from objects of other classes of
interest (i.e. String).
<snip>

The reason for the global transformation table is basically being able
to handle any kind of "type" not just Classes. I like to think of ruby
as object based more than Class based.

Anyway, even if your approach is more conservative, I'd appreciate if it
was blessed[1].

I wonder what matz think of this stuff.


[1]
what I'd really like to see is multimethods. Just specialize #new and
everything goes fine ;)
 
P

Paul Brannan

class String
def self.from(obj); obj.to_s; end
def to(klass); klass.from_s(self); end
end

If you did this, then I guess you could consider this the
overhead for each class that other classes want to convert
to/from - String, Integer, Float, Array, etc.

I would think this more straight-forward approach would be more
likely accepted.

Would you be more amenable to a solution like:

class Object
def to(type, *args, &block)
return send("to_#{type}", *args, &block)
end

def to_String
return to_s
end
end

class String
def to_Integer
return Integer(self)
end

# etc.
end

This eliminates the global constant by using double-dispatch, plus
retains the ability to convert to a non-class type (e.g.
foo.to(Enumerable)).

Paul
 
E

Eric Mahurin

--- Paul Brannan said:
=20
Would you be more amenable to a solution like:
=20
class Object
def to(type, *args, &block)
return send("to_#{type}", *args, &block)
end
=20
def to_String
return to_s
end
end
=20
class String
def to_Integer
return Integer(self)
end
=20
# etc.
end
=20
This eliminates the global constant by using double-dispatch,
plus
retains the ability to convert to a non-class type (e.g.
foo.to(Enumerable)).
=20
Paul

Let's say you have some aribitrary class that you want to
convert to/from a string. With the above, you'd put the
to-string method in this class and the from-string method in
String. I think better encapsulation would be to put both of
these in this new aribitrary class. To do it this way and
force the method names to have the class name in them, you'd do
this:

class Object
def self.from(obj,*args,&block)
send("from_#{self}".to_sym,obj,*args,&block)
rescue
obj.send("to_#{self}".to_sym,*args,&block)
end
def to(klass,*args,&block)
send("to_#{klass}".to_sym,*args,&block)
rescue
klass.send("from_#{self.class}".to_sym,self,*args,&block)
end
def to_String
to_s
end
def to_Integer
to_i
end
end


class Xyz
def self.from_String(s,base=3D10)
... make a Xyz from s ...
end
def to_String(base=3D10)
... make a String from self ...
end
end


Of course in the above, "klass" doesn't have to be a Class, but
it does need to respond to the right from_* method.

I'm not sure of the value of using from_String/to_String over
from_s/to_s. I think you'll have just a few classes that
you'll have many classes converting from/to them. You'll
never be able to convert from one arbitrary class to another.=20
Also, the above code is kind of ugly forming method names and
trying two different methods (not very duck-type like) - but I
could get over it.

Would this work with the non-class types you are talking about?




=09
=09
______________________________________________________
Click here to donate to the Hurricane Katrina relief effort.
http://store.yahoo.com/redcross-donate3/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top