Adam Shelly wrote:
> I just saw this quote in pickaxe, which helped clarify my thinking:
>
> "Ranges can be constructed using objects of any type, as long as the
> objects can be compared using their <=> operator and they support the
> succ method to return the next object in sequence. "
>
> But a<=>a.succ != -1 for all a, especially if a is a String.
> This causes r.find{|a| !r.member?(a)} to return non-nil for some
> ranges, which is unexpected, or possibly just plain wrong.
>
> So I think I like your suggestion, which I would boil down to:
> Change the requirement as follows: "Ranges can be constructed from
> objects of any class which supports #succ and #cmp, where
> a.cmp(a.succ)==-1 for all a."

Very nicely put. I wish I were as gifted at explaining things. Thanks
Adam.

> For numeric classes #cmp is an alias for #<=>.  For strings #cmp is a
> custom function, matching the string succession generator.
> And for your own classes you can write your own #cmp, which does not
> have to match #<=>.  For instance:
> President.new("Kennedy").cmp President.new("Nixon") #=> -1  (Nixon came later)
> President.new("Kennedy")<=>  President.new("Nixon") #=> 1  (but
> Kennedy is greater)

:-)

> The issue I see is that I don' t know if it is possible to write a
> valid #cmp for all cases.
> What is the result of 'a'.cmp('0') ?    You can't ever get a '0' with
> 'a'.succ.  So is '0' before or after 'a'?
>
> I suppose that you could require that a.cmp b returns nil when
> a=a.succ will never produce b.  Then Range#member? becomes a test for
> set membership, just like enum#member?:
> class Range
>   def member? v
>     (f=first.cmp(v) && f<=0  && l=last.cmp(v) && l>=0)
>   end
> end

Yes that's exactly it. The nice thing about having #cmp seperate from
#<=> is you can have optimizations to determing this, and it can be
uses by the #member? method.

> So yes, I like this suggestion.
> And I just realized it it may not be orthogonal to the other:
>  The suggested change leaves no way to test for Interval inclusion in
> those cases where the interval and the sequence are different (like
> 'a'..'bb').   So perhaps there still should be a method of testing
> Range inclusion using <=>. (my suggestion is #spans?)
> ('a'..'bb').member? 'z' #=> true
> ('a'..'bb').spans? 'z' #=> false
> ('a'..'bb').member? 'aardvark' #=> false
> ('a'..'bb').spans? 'aardvark' #=> true

Good point. Boundry checks with #<=>, irregardelss of membership, would
still be useful.

Thanks Adam.

T.