[BUG] string range membership

W

Warren Brown

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 Brown
 
B

Brian Schröder

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 Brown

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
 
J

Joel VanderWerf

Brian said:
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.
=20
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"
=3D> "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"
=3D> "1.6.4".."1.8.3"

I guess it is just impractical for Range#member? to test using #succ.

--=20
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: [BUG] string range membership"
| 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.
 
P

Paul

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>
 
T

Trans

I'm not sure how to solve this.

This was discussed sometime ago. The solution (mostly arrived at by
Peter Vanbroekhoven) 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.
 

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

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,241
Members
46,831
Latest member
RusselWill

Latest Threads

Top