Forum: Ruby string range membership

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
warrenbrown (Guest)
on 2005-11-23 08:44
(Received via mailing list)
All,

    I ran across some code that was trying to validate that an integer
was in a given range, however the integer and the range were Strings.
The problem boils down to this:

>ruby -e "p ('1'..'10').member?('2')"
false

    Given that...

>ruby -v -e "('1'..'10').each {|s| p s}"
ruby 1.8.2 (2004-12-25) [i386-mswin32]
"1"
"2"
"3"
"4"
"5"
"6"
"7"
"8"
"9"
"10"

    ...it seems like ('1'..'10').member?('2') should return true.  The
problem lies in range.c, in the range_each_func() method.  This method
starts with the first value, then calls succ() to get the next value,
breaking out of the loop when the value is no longer less than or equal
to the ending value (or strictly less than the ending value on an
exclusive range).  Unfortunately, for the given string range this
happens immediately, since '2' > '10'.

    I suppose that it could be argued that this is not a bug, but that
would be a difficult argument to win.  Also, I need to make sure that
this is still a bug in the latest version of Ruby.  Unfortunately, I'm
too sleepy to investigate further or create a patch for this tonight,
but I'll try to work on it some more tomorrow night (assuming nobody
else fixes it first).

    - Warren B.
ruby.brian (Guest)
on 2005-11-23 09:56
(Received via mailing list)
On 23/11/05, Warren B. <removed_email_address@domain.invalid> wrote:
>
> "9"
>     I suppose that it could be argued that this is not a bug, but that
> would be a difficult argument to win.  Also, I need to make sure that
> this is still a bug in the latest version of Ruby.  Unfortunately, I'm
> too sleepy to investigate further or create a patch for this tonight,
> but I'll try to work on it some more tomorrow night (assuming nobody
> else fixes it first).
>
>     - Warren B.
>
>
>

I'd argue that it is not a bug, as there is no unique isomorphie from
strings to integers. Some well known functions would be hex, octal and
decimal encoding. I.e. '2' ... '10' could be understood as 2 ... 8, or
2 ... 16 or 2 ... 10 or error ... 2 depending on the base.

Only you can now what the string means, so convert it to an integer
and do the range test on integers.

So I think you should save your time on creating the patch and
preferably fix the application code.

Brian

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/
vjoel (Guest)
on 2005-11-23 10:20
(Received via mailing list)
Brian Schröder wrote:
> I'd argue that it is not a bug, as there is no unique isomorphie from
> strings to integers. Some well known functions would be hex, octal and
> decimal encoding. I.e. '2' ... '10' could be understood as 2 ... 8, or
> 2 ... 16 or 2 ... 10 or error ... 2 depending on the base.
>
> Only you can now what the string means, so convert it to an integer
> and do the range test on integers.

I was going to make that argument, but I realized that #each (by way of
#succ) *does* have some extra knowledge (or assumptions) about strings,
and it's pretty smart:

irb(main):003:0> ('1.1'..'10.1').each {|s| p s}
"1.1"
"1.2"
"1.3"
"1.4"
...
...
"9.6"
"9.7"
"9.8"
"9.9"
"10.0"
"10.1"
=> "1.1".."10.1"
irb(main):004:0> ('1.6.4'..'1.8.3').each {|s| p s}
"1.6.4"
"1.6.5"
"1.6.6"
"1.6.7"
"1.6.8"
"1.6.9"
"1.7.0"
"1.7.1"
"1.7.2"
"1.7.3"
"1.7.4"
"1.7.5"
"1.7.6"
"1.7.7"
"1.7.8"
"1.7.9"
"1.8.0"
"1.8.1"
"1.8.2"
"1.8.3"
=> "1.6.4".."1.8.3"

I guess it is just impractical for Range#member? to test using #succ.
matz (Guest)
on 2005-11-23 10:20
(Received via mailing list)
Hi,

In message "Re: [BUG] string range membership"
    on Wed, 23 Nov 2005 15:41:32 +0900, "Warren B."
<removed_email_address@domain.invalid> writes:
|    I ran across some code that was trying to validate that an integer
|was in a given range, however the integer and the range were Strings.

include? and member? compares with beg <= val <= end, which is
dictionary order for strings.  Unfortunately strings generated from
using succ is not in dictionary order.  I'm not sure how to solve
this.

							matz.
Paul M. (Guest)
on 2005-11-23 10:28
(Received via mailing list)
Just playing with it around but...


irb(main):057:0> ('1'..'10').each { |i| puts i }
1
2
3
4
5
6
7
8
9
10
=> "1".."10"
irb(main):058:0> ('1'..'10').each { |i| case when i == '2' ; puts "two"
else puts i end}
1
two
3
4
5
6
7
8
9
10
=> "1".."10"
irb(main):059:0>
transfire (Guest)
on 2005-11-23 15:04
(Received via mailing list)
> I'm not sure how to solve this.

This was discussed sometime ago. The solution (mostly arrived at by
Peter V.) is to use a different comparision method. In
Facets you'll find the #cmp method, which is part of the base methods,
and which is used by the Interval class --a true Interval as opposed to
what Range is.

  def cmp(other)
    return -1 if length < other.length
    return 1 if length > other.length
    self <=> other
  end

Of course this won't be of use to tuple forms like "1.18.12", but in
such cases a Tuple object is in order anyway.

T.
This topic is locked and can not be replied to.