Forum: Ruby Range#member? Oddity

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.
James G. (Guest)
on 2006-01-13 22:50
(Received via mailing list)
I'm not understanding what I am seeing here.  Can anyone please
explain why the last line of this session gives *false* as an answer?

 >> range = ("1".."10")
=> "1".."10"
 >> range.to_a
=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
 >> range.member?("1")
=> true
 >> range.member?("2")
=> false

James Edward G. II
Daniel B. (Guest)
on 2006-01-13 22:56
(Received via mailing list)
James Edward G. II wrote:
> => false
>
> James Edward G. II

I cannot duplicate this with 1.8.2 or 1.8.4.

- Dan
James G. (Guest)
on 2006-01-13 22:59
(Received via mailing list)
On Jan 13, 2006, at 2:54 PM, Daniel B. wrote:

>> => false
>> James Edward G. II
>
> I cannot duplicate this with 1.8.2 or 1.8.4.

Odd.  I'm using 1.8.4:

$ ruby -v
ruby 1.8.4 (2005-12-24) [powerpc-darwin8.3.0]

James Edward G. II
Justin C. (Guest)
on 2006-01-13 22:59
(Received via mailing list)
It happens in 1.8.2 for me. It shows "1" and "10" as being in the range,
but nothing else.

[justinc@justinc-dsktp ~]$ ruby -v
ruby 1.8.2 (2004-12-25) [i586-linux-gnu]
[justinc@justinc-dsktp ~]$ irb
irb(main):001:0> range = ("1".."10")
=> "1".."10"
irb(main):002:0> range.to_a
=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
irb(main):003:0> range.member?("1")
=> true
irb(main):004:0> range.member?("2")
=> false
irb(main):005:0> range.member?("10")
=> true
irb(main):006:0>


Note that range _isn't_ getting converted into an array.

-Justin
Daniel H. (Guest)
on 2006-01-13 23:02
(Received via mailing list)
Daniel B. wrote:
> James Edward G. II wrote:
>> I'm not understanding what I am seeing here.  Can anyone please
>> explain why the last line of this session gives *false* as an answer?
>> <snip>
>
> I cannot duplicate this with 1.8.2 or 1.8.4.
>
> - Dan

I'm getting this too. 1 and 10 both return true, everything else
returns false.

$ ruby -v
ruby 1.8.4 (2005-12-24) [powerpc-darwin8.3.0]
J. Ryan S. (Guest)
on 2006-01-13 23:05
(Received via mailing list)
On Jan 13, 2006, at 3:54 PM, Daniel B. wrote:

>> => false
>> James Edward G. II
>
> I cannot duplicate this with 1.8.2 or 1.8.4.

I can.

$ ruby -v
ruby 1.8.4 (2005-12-24) [powerpc-darwin8.3.0]

~ ryan ~
tokmak tokmak (Guest)
on 2006-01-13 23:05
(Received via mailing list)
it gets odder:

irb(main):001:0> range = ("1".."10")
=> "1".."10"
irb(main):002:0> range.to_a
=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
irb(main):003:0> range.member?("1")
=> true
irb(main):004:0> range.member?("2")
=> false
irb(main):005:0> range.member?("4")
=> false
irb(main):006:0> range.member?("9")
=> false
irb(main):007:0> range.member?("10")
=> true
irb(main):008:0> range.include?("2")
=> false
irb(main):009:0> range.include?("1")
=> true
irb(main):010:0> range === "1"
=> true
irb(main):011:0> range === "2"
=> false
irb(main):012:0> RUBY_VERSION
=> "1.8.2"


2006/1/13, James Edward G. II <removed_email_address@domain.invalid>:
Daniel B. (Guest)
on 2006-01-13 23:08
(Received via mailing list)
James Edward G. II wrote:
>>>  >> range.member?("1")
>
> $ ruby -v
> ruby 1.8.4 (2005-12-24) [powerpc-darwin8.3.0]
>
> James Edward G. II
>

Whoops, I misspoke.  I was using numbers, not strings.  I do, in fact,
get the
same behavior in 1.8.4 (and 1.8.2).

/me guesses randomly that it has something to do with stringified
numbers in
particular.

Regards,

Dan
Dan H. (Guest)
on 2006-01-13 23:11
(Received via mailing list)
I don't know for sure but my guess has something to do with numbers vs
characters and whether "1".."10" expands to the same thing that 1..10
does.

>>range=(1..10)
1..10
>>range.member?(1)
true
>>range.member?(2)
true
>>range.member?(3)
true
>>puts range
1..10

   Huh???

-dwh-

On Fri, 2006-01-13 at 16:01, Daniel H. wrote:

> I'm getting this too. 1 and 10 both return true, everything else
> returns false.
>
> $ ruby -v
> ruby 1.8.4 (2005-12-24) [powerpc-darwin8.3.0]

--
I not only live each endless day in grief, but live each day
thinking about living each day in grief.
                                              -- C.S. Lewis

Daniel W. Hinz         Xerox Corp: XOG/SEBU/MCD/EIDC/ISM&D
MS: 111-03J            e-mail:      removed_email_address@domain.invalid
800 Phillips Road     TEL:          585.422.8078
Webster, NY 14580
Matthew D. (Guest)
on 2006-01-13 23:18
(Received via mailing list)
James Edward G. II wrote:
> => false
>
> James Edward G. II
>
>
>
Hi,

There was some discussion about this in the recent past.  If my memory
serves me right (certainly an infrequent happening), the issue that
you're running into is that Range#member?  is implemented as:
class Range
    def member?(val)
       if self.exclude_end?
           (self.first <= val) and (val < self.last)
       else
          (self.first <= val) and (val <= self.last)
       end
    end
end

You should find this in both 1.8.2 and 1.8.4 I think.

There's a previous thread on ruby-talk about it, here's a link to
somewhere near the conclusion of the discussion:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...

Matthew
James G. (Guest)
on 2006-01-13 23:30
(Received via mailing list)
On Jan 13, 2006, at 3:16 PM, Matthew D. wrote:

> There's a previous thread on ruby-talk about it, here's a link to
> somewhere near the conclusion of the discussion:
> http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...

This cleared it up for me.  Thank you.

I was aware of the old system, where member?() and include?() had
separate meanings and just didn't know it had been abandoned.

James Edward G. II
David V. (Guest)
on 2006-01-14 00:01
(Received via mailing list)
On Fri, 13 Jan 2006 22:16:14 +0100, Matthew D.
<removed_email_address@domain.invalid>
wrote:

>> >> range.member?("2")
> you're running into is that Range#member?  is implemented as:
> You should find this in both 1.8.2 and 1.8.4 I think.
>
> There's a previous thread on ruby-talk about it, here's a link to
> somewhere near the conclusion of the discussion:
> http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...
>
> Matthew
>


This would seem to make it an oddity of String#succ, that behaving
automagically, not generating successors with respect to String
comparison.

E.g. for any Integers i, i.succ > i. For some strings, that does't hold
true.

Bottom line: Don't use strings when you're really using numbers. Like in
mathemathical contexts. D'oh. You could possibly hack around that in
Range
code to provide for data types where generating successors is
inconsistent
with comparison, but I wouldn't like to see that.

David V.
James G. (Guest)
on 2006-01-14 00:01
(Received via mailing list)
On Jan 13, 2006, at 3:58 PM, David V. wrote:

> You could possibly hack around that in Range code to provide for
> data types where generating successors is inconsistent with
> comparison, but I wouldn't like to see that.

It's not too tough in this case:

 >> ("1".."10").to_a.include?("2")
=> true

James Edward G. II
David V. (Guest)
on 2006-01-14 00:16
(Received via mailing list)
On Fri, 13 Jan 2006 23:00:37 +0100, James Edward G. II
<removed_email_address@domain.invalid> wrote:

>
> James Edward G. II
>


Yes, that always works, but it beats the point of having first class
ranges as opposed to just having a pythonesque range function in the
first
place. I'd personally rather coerce the strings to numbers if I know
they
represent such to get more type safety and possibly some execution speed
too.

David V.
J. Ryan S. (Guest)
on 2006-01-14 00:49
(Received via mailing list)
The rdoc needs to be updated for Range#include? and Range#member? then.

~ ryan ~
David V. (Guest)
on 2006-01-14 01:04
(Received via mailing list)
No, those methods work perfectly. The behaviour of String is the problem
here.

On Fri, 13 Jan 2006 23:46:26 +0100, J. Ryan S. 
<removed_email_address@domain.invalid>
James G. (Guest)
on 2006-01-14 01:19
(Received via mailing list)
On Jan 13, 2006, at 5:04 PM, David V. wrote:

> No, those methods work perfectly. The behaviour of String is the
> problem here.

???  How exactly is it that you believe String should behave?

James Edward G. II
David V. (Guest)
on 2006-01-14 01:55
(Received via mailing list)
On Sat, 14 Jan 2006 00:17:26 +0100, James Edward G. II
<removed_email_address@domain.invalid> wrote:

> On Jan 13, 2006, at 5:04 PM, David V. wrote:
>
>> No, those methods work perfectly. The behaviour of String is the
>> problem here.
>
> ???  How exactly is it that you believe String should behave?
>
> James Edward G. II
>


Well, to work well in Ranges, for any String s, s.succ > s must hold
true.

David V.
James G. (Guest)
on 2006-01-14 02:01
(Received via mailing list)
On Jan 13, 2006, at 5:54 PM, David V. wrote:

>> James Edward G. II
>>
>
>
> Well, to work well in Ranges, for any String s, s.succ > s must
> hold true.

I'm pretty sure we don't want to change the meaning of String
comparisons at this point.  succ() just doesn't happened to be
defined under those terms, because then it would be a lot less useful
to us.  It's hard for me to see any of that as "broken".

James Edward G. II
unknown (Guest)
on 2006-01-14 02:20
(Received via mailing list)
On Jan 13, 2006, at 6:54 PM, David V. wrote:
> Well, to work well in Ranges, for any String s, s.succ > s must
> hold true.

How about having Range use Object#strict_succ to generate
its sequence?  Define String#strict_succ as needed to guarantee
s.succ > s and then alias strict_succ to succ for other classes
(such as Fixnum) so they don't break when used in a Range.


Gary W.
Logan C. (Guest)
on 2006-01-14 02:29
(Received via mailing list)
On Jan 13, 2006, at 7:18 PM, removed_email_address@domain.invalid wrote:

>
> Gary W.
>
>
>
>

This seems to me to make the problem worse. People expect the values
generated by String#succ to be in the array when doing a to_a for
instance. I believe the real solution would be to bring back the
distinction between member and include (possibly with a new name for
the method with the functionality of #member).
David V. (Guest)
on 2006-01-14 02:35
(Received via mailing list)
On Sat, 14 Jan 2006 01:01:21 +0100, James Edward G. II
<removed_email_address@domain.invalid> wrote:

> On Jan 13, 2006, at 5:54 PM, David V. wrote:
>
> I'm pretty sure we don't want to change the meaning of String
> comparisons at this point.  succ() just doesn't happened to be defined
> under those terms, because then it would be a lot less useful to us.
> It's hard for me to see any of that as "broken".
>
> James Edward G. II
>


I never said anything the like, the code breakage would be inexcusable.

My point is it's String that behaves erreneously in this context and
isn't
suitable for use in symbolic ranges, not a bug in Range code - the posts
were an objection to people wanting to mess up the Range interface.

David V.
David V. (Guest)
on 2006-01-14 02:38
(Received via mailing list)
On Sat, 14 Jan 2006 01:26:22 +0100, Logan C.
<removed_email_address@domain.invalid>
wrote:

>> s.succ > s and then alias strict_succ to succ for other classes
> generated by String#succ to be in the array when doing a to_a for
> instance. I believe the real solution would be to bring back the
> distinction between member and include (possibly with a new name for the
> method with the functionality of #member).
>


I agree that the change proposed would break Range#to_a for strings,
which
I can imagine being used. The real solution would be people coding
sanely
and using Integers to represent integers, and Strings to represent text
-
using unsuitable data types resulting in a bug is arguably not a
language
fault.

David V.
unknown (Guest)
on 2006-01-14 02:56
(Received via mailing list)
On Jan 13, 2006, at 7:26 PM, Logan C. wrote:
> This seems to me to make the problem worse. People expect the
> values generated by String#succ to be in the array when doing a
> to_a for instance. I believe the real solution would be to bring
> back the distinction between member and include (possibly with a
> new name for the method with the functionality of #member).

It seems strange to want Range to behave like an interval and to
also want Range#to_a to create a list of elements that don't all
belong in that same interval.

I understand the desire to generate a sequence of strings
as defined by the current behavior of String#succ but I don't understand
why you would want to use a Range object as a shortcut to that sequence.
That particular sequence really has nothing to do with an interval
or the ordering defined by String#<=>


Gary W.
David V. (Guest)
on 2006-01-14 03:05
(Received via mailing list)
On Sat, 14 Jan 2006 01:53:37 +0100, <removed_email_address@domain.invalid> 
wrote:

>
> It seems strange to want Range to behave like an interval and to
> also want Range#to_a to create a list of elements that don't all
> belong in that same interval.

It's a Convenient Hack (tm), and those are extremely hard to weed out
once
they catch hold. It's the same as when people use #eval that must a
nightmare to the brave folks working on optimizing YARV (I want my clean
blocks!) instead of the much cleaner metaprogramming facilities in Ruby
that let you achieve 99% of what I've seen hacked with evals anyway.
(Very
obfuscated eval hacks notwithstanding).

David V.
Logan C. (Guest)
on 2006-01-14 03:38
(Received via mailing list)
On Jan 13, 2006, at 7:53 PM, removed_email_address@domain.invalid wrote:

> I understand the desire to generate a sequence of strings
> as defined by the current behavior of String#succ but I don't
> understand
> why you would want to use a Range object as a shortcut to that
> sequence.
> That particular sequence really has nothing to do with an interval
> or the ordering defined by String#<=>

As far as I can tell two classes(Well 4ish) have #succ methods.
Integers and Strings. For all integers x, x has the property that
x.succ > x is true.
There exists at least one String x for which x.succ > x is true, and
at least one String x for which x.succ > x is false. So the question
is, what is a Range? Is it a pair of end-points for checking between-
ness? Is it the set of all values between those end points? If it is
the second, I would argue that #to_a is meaningless for strings
because there are end points for which the set would have infinite
cardinality (i.e. ("a".."b"), the set of strings matched by /^a+\z/
at least is in that "range".).
Bertram S. (Guest)
on 2006-01-14 03:53
(Received via mailing list)
Hi,

Am Samstag, 14. Jan 2006, 05:49:56 +0900 schrieb James Edward G. II:
> => false
irb(main):006:0> 2 < 10
=> true
irb(main):007:0> "2" < "10"
=> false

Bertram
Hal F. (Guest)
on 2006-01-14 05:08
(Received via mailing list)
James Edward G. II wrote:
> => false
In short: Don't do this.

String ranges are very weird. They don't obey the rule that
x.succ > x.

Since "2" > "10", "2" is considered outside the range entirely.

If you really want to do this kind of thing, say
("1".."10").to_a.member?("2")


Hal
Christer N. (Guest)
on 2006-01-14 07:29
Hal F. wrote:
> James Edward G. II wrote:
>> => false
> In short: Don't do this.
>
> String ranges are very weird. They don't obey the rule that
> x.succ > x.
>
> Since "2" > "10", "2" is considered outside the range entirely.
>
> If you really want to do this kind of thing, say
> ("1".."10").to_a.member?("2")
>
>
> Hal

If the two strings have the same length, the weirdness disappears.

Christer
Pit C. (Guest)
on 2006-01-14 10:48
(Received via mailing list)
Christer N. schrieb:
> If the two strings have the same length, the weirdness disappears.
Very good point!

Regards,
Pit
unknown (Guest)
on 2006-01-14 13:16
(Received via mailing list)
Hi --

On Sat, 14 Jan 2006, Christer N. wrote:

>> If you really want to do this kind of thing, say
>> ("1".."10").to_a.member?("2")
>>
>>
>> Hal
>
> If the two strings have the same length, the weirdness disappears.

But other weirdnesses arise :-)

irb(main):019:0> ("A".."z").member?("g")
=> true
irb(main):020:0> ("A".."z").to_a.include?("g")
=> false


David

--
David A. Black
removed_email_address@domain.invalid

"Ruby for Rails", from Manning Publications, coming April 2006!
http://www.manning.com/books/black
Stefan W. (Guest)
on 2006-01-14 16:48
(Received via mailing list)
removed_email_address@domain.invalid wrote:
  > But other weirdnesses arise :-)
>
> irb(main):019:0> ("A".."z").member?("g")
> => true
> irb(main):020:0> ("A".."z").to_a.include?("g")
> => false
>
>
> David
>

IMO, the thing at fault here is that Ranges include Enumerable. From a
mathematical POV, that's nonsense, and that's why oddities like this
exist. Things that can be used for ranges can not used like an
Enumarable (rational..rational, float..float), because those don't have
#succ instance methods... and String ranges behave oddly because the
member? checks use the <=> behaviour, and each uses succ...

Regards,
Stefan
unknown (Guest)
on 2006-01-14 17:39
(Received via mailing list)
On Sat, 14 Jan 2006, Stefan W. wrote:

> IMO, the thing at fault here is that Ranges include Enumerable. From a
> mathematical POV, that's nonsense, and that's why oddities like this exist.
> Things that can be used for ranges can not used like an Enumarable
> (rational..rational, float..float), because those don't have #succ instance
> methods... and String ranges behave oddly because the member? checks use the
> <=> behaviour, and each uses succ...


but enumerable means neither monotonically increasing nor finite.  so
that a
range includes enumerable means only that one may start, but perhaps
never
finish, to count the elements in the range.  i think this is accurate
isn't
it?

cheers.

-a
Stefan W. (Guest)
on 2006-01-14 18:07
(Received via mailing list)
removed_email_address@domain.invalid wrote:
> On Sat, 14 Jan 2006, Stefan W. wrote:
> but enumerable means neither monotonically increasing nor finite.  so
> that a
> range includes enumerable means only that one may start, but perhaps never
> finish, to count the elements in the range.  i think this is accurate isn't
> it?
>
> cheers.
>
> -a

Enumerable means that you can map the natural numbers to your set in an
invertable way, and that's not possible for ranges of real numbers.
Being enumerable means the possibility of a "succ" operation, and
therefore of traversing the whole set with "each" (which may take
infinite time). Being enumerable implies a working each, now try
(1.0..2.0).each...

Regards,
Stefan
David V. (Guest)
on 2006-01-14 19:42
(Received via mailing list)
On Sat, 14 Jan 2006 17:03:59 +0100, Stefan W. 
<removed_email_address@domain.invalid>
wrote:

>
> Enumerable means that you can map the natural numbers to your set in an
> invertable way, and that's not possible for ranges of real numbers.
> Being enumerable means the possibility of a "succ" operation, and
> therefore of traversing the whole set with "each" (which may take
> infinite time). Being enumerable implies a working each, now try
> (1.0..2.0).each...
>
> Regards,
> Stefan
>


There's nothing wrong with enumerating ranges, you can well have
intervals
of natural numbers or other countably infinite sets. Bounded intervals
of
countable or countably infinite sets are finite sets and may be (not
necessarily though) enumerated in order using the successor operation,
which exists if the elements of the interval are well-ordered. It's not
exactly an edge case, and distinguishing between ranges with finite and
infinite element counts

The issue with strings is that their ordering does _not_ make them such
a
well-ordered countably infinite set. Because strings are compared using
textual comparison, as was already noted in this thread, for every two
given strings, there exists an infinite amount of strings between them.
For the sake of completion, strings in fact _are_ a countably infinite
set, but their ordering doesn't respect any mapping of strings to
natural
numbers.

Given this, the string successor operation doesn't even make sense from
the mathemathical point of view. It is in this context defined ad hoc,
and
it's usefulness lies in contexts of text processing.

Using a string Range is a convenient shortcut, but it's a _hack_.
Nothing
more, nothing less. Strings don't have a solid foundation for use in
maths. If you expect strings to behave as numbers, you're wrong, they're
not supposed to, any bugs are your fault, not the fault of the Ruby core
API. Moan and suffer.

David V.
Stefan W. (Guest)
on 2006-01-14 20:55
(Received via mailing list)
David V. wrote:
> There's nothing wrong with enumerating ranges, you can well have
> intervals of natural numbers or other countably infinite sets. Bounded
> intervals of countable or countably infinite sets are finite sets and
> may be (not necessarily though) enumerated in order using the successor
> operation, which exists if the elements of the interval are
> well-ordered. It's not exactly an edge case, and distinguishing between
> ranges with finite and infinite element counts

Your messages seems to have been truncated here. Also, you're wrong,
bounded intervals of countably infinite sets do not have to be finite.
Consider [0,1] in the rational numbers. Rational numbers are countable,
the range is bounded, but the set is not finite (it is still countably
infinite).

> Using a string Range is a convenient shortcut, but it's a _hack_.
> Nothing more, nothing less. Strings don't have a solid foundation for
> use in maths. If you expect strings to behave as numbers, you're wrong,
> they're not supposed to, any bugs are your fault, not the fault of the
> Ruby core API. Moan and suffer.
>
> David V.

Range including Enumerable is a fault of the ruby core API, because
(1.0..20) pretends to be enumerable (is_a? returns true, responds_to?
:each returns true), but is not.

Regards, Stefan (who never had problems with string ranges, only with
Range including Enumerable)
Robert R. (Guest)
on 2006-01-14 20:58
(Received via mailing list)
James Edward G. II schrieb:

> => false
>
> James Edward G. II
>
>
>
I don't know why to_a is working here.
I know that a range from '1' to '10' could be like you described, but it
doesn't make a logical sense I think.
like ('a'..'ah') what should that be?
[a-255.chr] and all a[0.chr-255.chr]?

This is what I think, of course.
Dunno if it makes sense :>

MfG
Retze
David V. (Guest)
on 2006-01-14 21:19
(Received via mailing list)
On Sat, 14 Jan 2006 19:54:55 +0100, Stefan W. 
<removed_email_address@domain.invalid>
wrote:

>
> [snip]
>
> Range including Enumerable is a fault of the ruby core API, because
> (1.0..20) pretends to be enumerable (is_a? returns true, responds_to?
> :each returns true), but is not.

That is, of course, misleading, but ranges are very commonly used in the
special cases when they are indeed enumerable, so I can understand why
it's defined that way in the core API. Library design isn't there to
replace programmer brains. You could theorethically check whether the
range endpoints are comparable, and whether the starting one defines
#succ, and then mix in Enumerable to the Ranges that indeed are to get
more strictly correct behaviour.

David V.
unknown (Guest)
on 2006-01-14 23:44
(Received via mailing list)
On Sun, 15 Jan 2006, Stefan W. wrote:

> Enumerable means that you can map the natural numbers to your set in an
> invertable way, and that's not possible for ranges of real numbers. Being
> enumerable means the possibility of a "succ" operation, and therefore of
> traversing the whole set with "each" (which may take infinite time). Being
> enumerable implies a working each, now try (1.0..2.0).each...

that is certainly true.  however, there are no real numbers in computing
-
only rational numbers and rationals are countably infinite

   http://mathworld.wolfram.com/CountablyInfinite.html
   http://mathworld.wolfram.com/RationalNumber.html

while (1.0..2.0).each{|x| p x} cannot work with real numbers it can
certainly
work with a set of rational numbers - in computing terms you can
certainly
count from 1.0 to 2.0 by adding the floating point epsilon.  my opinion
is
that we're talking computer languages here, not mathematicaly ones, and
so any
idea of being 'correct' must be in the context of what computers can do.

in any case, ruby makes no guarantee that

   (a..b).each{|x| p x}

will complete, so even if we had real numbers that would still be a
valid
construct - though mixing in Enumerable into Range would be strange then
- but
it not the case.

cheers.

-a
Logan C. (Guest)
on 2006-01-14 23:56
(Received via mailing list)
On Jan 14, 2006, at 4:41 PM, removed_email_address@domain.invalid wrote:

> in any case, ruby makes no guarantee that
>
>   (a..b).each{|x| p x}
>
> will complete, so even if we had real numbers that would still be a
> valid
> construct - though mixing in Enumerable into Range would be strange
> then - but
> it not the case.

Here's a thought, get rid of #each and Enumerable from Range, but add
in a #to_enum method (no args) that would return an Enumerator for
the range. This way programmers could say, "I solemnly swear that I
know  iterating thru a range of strings doesn't make sense with
include, but I want to do it anyway."

("ab".."xyz").to_enum.to_a # etc...

On second thought, that looks unnecessary.

How about:

% cat a.rb
("abc".."wxyz").to_a

% ruby -w a.rb
warning: You fool! By doing this you know include? will make no sense!

Maybe not.

Stupid ranges. Hmph.
unknown (Guest)
on 2006-01-15 00:31
(Received via mailing list)
On Sun, 15 Jan 2006, David V. wrote:

> You could theorethically check whether the range endpoints are comparable,
> and whether the starting one defines #succ, and then mix in Enumerable to
> the Ranges that indeed are to get more strictly correct behaviour.

but that prevents me from doing this

     harp:~ > cat a.rb
     require "yaml"

     e = Float::EPSILON
     r = 1.0 .. 1.0000000000000091038288019262836314737796783447265625

     class Float
       def succ() self + EPSILON end
     end

     a = r.to_a

     y "r.min" => ("%52.52f" % r.min)
     y "r.max" => ("%52.52f" % r.max)
     y "r.to_a.size" => r.to_a.size



     harp:~ > ruby a.rb
     ---
     r.min: "1.0000000000000000000000000000000000000000000000000000"
     ---
     r.max: "1.0000000000000091038288019262836314737796783447265625"
     ---
     r.to_a.size: 42


i can easily imagine uses for code just like that.  i think it's best if
ruby
will let us run quickly with knives.

regards.

-a
David V. (Guest)
on 2006-01-15 01:13
(Received via mailing list)
On Sat, 14 Jan 2006 23:29:18 +0100, <removed_email_address@domain.invalid> 
wrote:

>
> i think it's best if ruby
> will let us run quickly with knives.
>
> regards.
>
> -a


Oh, I agree with that myself. I'm just objecting to the people that
complain when they trip ;P

David V.
Adam S. (Guest)
on 2006-01-16 07:34
(Received via mailing list)
I was messing around with this range behavior after the last discussion,
and
I came up some code that made ranges act, at least to me:
I made range.member?(x0  ==  range.to_a.member?(x) and range.spans?(x)
==
(range.first < x < range.last)  .
I had to implement string.follows? s  which works like the spaceship
operator but uses the string succession rules:

class Range
  def member?(x)
    f=first.follows?(x) and f<0  && l=last.follows?(x) and l>0
  end
  def spans?(x)
    (first<=>x) <=0 && (last<=>x) >=0
  end
end

class Object
  def follows?  other
    self<=>other if self.respond_to? :succ
  end
end

class String
  def is_numeric?
    /^-?\d+$/.match(self)  ? true : false
  end

  def case
    return :upcase if self==self.upcase
    return :downcase if self==self.downcase
    return :mixcase
  end

  def follows? str
    return 0 if str == self
    return 1 if self.is_after? str
    return -1 if str.is_after? self
    return nil
  end

  def is_after? str   #not pretty, but it seems to work...
    #split into groups of letters and digits
    firstparts = str.split(/\W/).map{|c|c.split(/(\d+)/)}.flatten
    lastparts = self.split(/\W/).map{|c|c.split(/(\d+)/)}.flatten
    matching = nil
    return false if lastparts.size != firstparts.size
    if firstparts == [] && lastparts == [] #both strings are all
punctuation
      firstparts = [str]; lastparts = [self]
    end
    #return false on first non-matching or non-following group
    firstparts.each_index do |i|
      next if firstparts[i] == lastparts[i]
      firstparts[i].split_by_case.zip(lastparts[i].split_by_case).each {
|f,l|
        return false if f==nil || l==nil
        next if (matching && l.format_match(f))
        if (f.is_numeric?)
          return false unless l.is_numeric? and l.to_i > f.to_i
        else
          return false if l.case != f.case || l.cmp(f) == -1
        end
        matching = true #this group is in the right order - the rest
just
have to match by format
      }
    end #each
    true
  end

  def split_by_case
    letters = split("").zip(downcase.split(""))
    same = (letters[0][0]!=letters[0][1])
    letters.inject([]){|a,l|
     if (same == (l[0]==l[1]))
        a[-1]+=l[0]
      else
        a<<l[0]
      end
      same = (l[0]==l[1])
      a
    }
  end
  def cmp str
    n = self.length<=>str.length
    return self<=>str if n==0
    n
  end
  def format_match str
    is_numeric? == str.is_numeric? && length == str.length
  end
end

require 'test/unit'
class TestRange < Test::Unit::TestCase
  def test_memberspan
    assert_equal(true, ('a'..'bb').member?('z') )
    assert_equal(false, ('a'..'bb').spans?('z'))
    assert_equal(false,('a'..'bb').member?('aardvark'))
    assert_equal(true,('a'..'bb').spans?('aardvark'))
    assert_equal(true,('a'..'bb').spans?('a'))
    assert_equal(true,('a'..'bb').spans?('bb'))
    assert_equal(false,(1..100).member?(101))
    assert_equal(false,(1..100).spans?(101))
    assert_equal(true,(1..100).member?(11))
    assert_equal(true,(1..100).spans?(11))
    assert_equal(true,(1.5..100).spans?(11))
    assert_equal(nil,(1.5..100).member?(11))
  end
end

-Adam
This topic is locked and can not be replied to.