Lexical Casts with Ruby

Discussion in 'Ruby' started by Rüdiger Sonderfeld, Sep 3, 2005.

  1. 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 <>
    Rüdiger Sonderfeld, Sep 3, 2005
    #1
    1. Advertising

  2. 2005/9/3, R=FCdiger 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 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
    Robert Klemme, Sep 3, 2005
    #2
    1. Advertising

  3. --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 --

    On Sun, 4 Sep 2005, [utf-8] R=C3=BCdiger Sonderfeld wrote:

    > 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

    --8323328-1020658632-1125761734=:19128--
    --8323328-1020658632-1125761734=:19128--
    David A. Black, Sep 3, 2005
    #3
  4. Rüdiger Sonderfeld

    Paul Brannan Guest

    On Sun, Sep 04, 2005 at 12:16:26AM +0900, R??diger Sonderfeld wrote:
    > 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
    Paul Brannan, Sep 7, 2005
    #4
  5. Rüdiger Sonderfeld

    Eric Mahurin Guest

    --- Paul Brannan <> wrote:

    > On Sun, Sep 04, 2005 at 12:16:26AM +0900, R??diger Sonderfeld
    > wrote:
    > > 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.
    > >=20
    > > Is there a way for lexical casts between types in Ruby?

    > Something like
    > > convert("10", Fixnum)
    > > =3D> 10

    >=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/
    Eric Mahurin, Sep 7, 2005
    #5
  6. Rüdiger Sonderfeld

    Paul Brannan Guest

    On Thu, Sep 08, 2005 at 02:09:32AM +0900, Eric Mahurin wrote:
    > 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
    Paul Brannan, Sep 8, 2005
    #6
  7. Rüdiger Sonderfeld

    Eric Mahurin Guest

    --- Paul Brannan <> wrote:

    > On Thu, Sep 08, 2005 at 02:09:32AM +0900, Eric Mahurin wrote:
    > > so you could do:
    > >=20
    > > "10".to(Integer) # 10
    > > String.from(10) # "10"
    > >=20
    > > If this is a good solution for you, maybe an RCR for this

    > more
    > > general conversion mechanism is in order.

    >=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/
    Eric Mahurin, Sep 8, 2005
    #7
  8. Eric Mahurin ha scritto:
    >>>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.

    >
    >
    > 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 ;)
    gabriele renzi, Sep 8, 2005
    #8
  9. Rüdiger Sonderfeld

    Paul Brannan Guest

    On Fri, Sep 09, 2005 at 03:49:59AM +0900, Eric Mahurin wrote:
    > 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
    Paul Brannan, Sep 8, 2005
    #9
  10. Rüdiger Sonderfeld

    Eric Mahurin Guest

    --- Paul Brannan <> wrote:

    > On Fri, Sep 09, 2005 at 03:49:59AM +0900, Eric Mahurin wrote:
    > > class String
    > > def self.from(obj); obj.to_s; end
    > > def to(klass); klass.from_s(self); end
    > > end
    > >=20
    > > 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.
    > >=20
    > > I would think this more straight-forward approach would be

    > more
    > > likely accepted.

    >=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/
    Eric Mahurin, Sep 8, 2005
    #10
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Eric
    Replies:
    3
    Views:
    1,122
  2. Collin VanDyck
    Replies:
    0
    Views:
    307
    Collin VanDyck
    Nov 30, 2004
  3. Collin VanDyck
    Replies:
    0
    Views:
    414
    Collin VanDyck
    Nov 30, 2004
  4. Ron

    Lexical convention

    Ron, Aug 30, 2003, in forum: C++
    Replies:
    0
    Views:
    378
  5. cricket

    Python lexical scanner

    cricket, Sep 25, 2003, in forum: Python
    Replies:
    0
    Views:
    442
    cricket
    Sep 25, 2003
Loading...

Share This Page