This make sense for things like :
irb(main):001:0> ["b", "aa", "d", "dd"].sort
=> ["aa", "b", "d", "dd"]
But doesn't for :
irb(main):002:0> ("B".."AA").each{ |x| print "#{x}," }
=> "B".."AA"
should produce : B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,=> "B".."AA" but "B" > "AA" => true
Unlike "B".."BA" ("B" > "BA" => false) :
irb(main):003:0> ("B".."BA").each{ |x| print "#{x}," }
B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AS,AT,AU,AV,AW,AX,AY,AZ,BA,=> "B".."BA"
Any advice to make "b".."aa" work as expected in ruby ?
I use
- irb 0.9.5(05/04/13) ruby 1.8.7
- (2009-06-12 patchlevel 174) [i486-linux]
- Linux 2.6.31-19-generic #56-Ubuntu SMP Thu Jan 28 01:26:53 UTC 2010 i686 GNU/Linux
The best way to do this is to subclass String and redefine the comparison operator to meet your needs. Then use your new class to make the range.
class MyString < String
def initialize str=""
super str
end
def <=>(other)
length_cmp = self.length <=> other.length
return length_cmp unless length_cmp == 0
super other
end
end
Now you can ensure that a column appears before another.
"b" < "aa" #=> false
MyString.new("b") < MyString.new("aa") #=> true
N.B.: Only the string on the left side of any comparison operator needs to be of class MyString:
MyString.new("b") < "aa" #=> true
"aa" > MyString.new("b") #=> false
It looks like Ruby is using succ
, but first it checks for end>=start
, and since that is false here, it doesn't even try.
Admittedly String#succ
is a weird beast here: a string's successor isn't always greater than the string, using its own comparison methods. So I'm not sure if this is technically a bug or not. It does look pretty confusing if you don't know this undocumented check, though.
Then again, judging by some of the other answers here, it looks like it does work as you expect in some versions of Ruby, so maybe it was fixed in 1.9?
It is true that in class String, <=> and String#succ are not completely harmonized
I suppose it would be nice if for each a, b where b eventually is produced from a.succ.succ..., it was also true that a <=> b returned -1. One reason this would be nice is that it is in fact precisely <=>
and succ
that are used to implement ranges in the first place. Consequently, as you have noted, a Ruby String range where .succ
would eventually complete the expansion doesn't work because a <=>
test contradicts it and terminates the loop.
So yes, the ordering, at least for String, that is defined by <=>
doesn't match the #succ
method ordering. This is one reason that some developers avoid using succ
.
This DOESN'T work in ruby 1.8.7 (2009-06-12 patchlevel 174) nor in ruby 1.9.1p376 (2009-12-07 revision 26041) [i486-linux]Any advice to make "b".."aa" work as expected in ruby ?
"b".."ba" does work...
irb(main):001:0> ("b".."ba").each {|x| print "#{x} "} b c d e f g h i j k l m n o p q r s t u v w x y z aa ab ac ad ae af ag ah ai aj ak al am an ao ap aq ar as at au av aw ax ay az ba => "b".."ba"
This was reported as a bug in Ruby here. The results depend on which version of Ruby you are running. There is a difference between versions 1.8.6 and 1.9.1.
来源:https://stackoverflow.com/questions/2267810/string-range-comparison-problem