From: Adam Shelly Date: 2005-12-02T15:57:11+09:00 Subject: Re: [BUG] string range membership On 12/1/05, Trans wrote: > Nice summary Warren. > > > I've offered the best solution generally possible for this issue: It > corrects the underlying cuase, fixes the inconsistant functionality and > maintains efficiency. What more can one ask? Nonetheless no one seems > interested in it. I tend to think the reason is becuase it introduces a > new method (#cmp), but since no one has even touched on it, how do I > know? I'm at a loss. Do people just not get it? Did I not explain it > well enough? Did I miss something? Or is that people just prefer to > stew around in their own preconceptions? > 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." 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 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 -Adam