有人可以解释一下为什么
('A'..'AB').to_a
#=> ["A", "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"]
但
('B'..'AB').to_a
#=> []
答案 0 :(得分:3)
string1..string2
会列出"所有"两个值之间的字符串 in"字典顺序" 。
更确切地说,它会在string1
上重复调用String#succ
,直到达到string2
。
"A".succ == "B"
"B".succ == "C"
# ...
"Z".succ == "AA"
"AA".succ == "AB"
另一方面,如果string2 < string1
(即&#34; string2
在string1
之前出现在字典&#34;)中,那么您将得到一个空的范围。 初次使用String#<=>
以检查范围是否为&#34;有效&#34; ,然后通过调用{{{{}}}达到string2
之间存在轻微差异1}} String#succ
。
如果您想生成该列表:string1
,那么您可以指定&#34;有效&#34;范围,然后删除第一个元素:
["B", "C", ... , "Z", "AA", "AB"]
假设您要使用两个字符串,这些字符串位于&#34;错误的顺序&#34; - 例如('A'..'AB').drop(1)
和"BA"
。请注意,此列表将计算&#34;向后&#34;:
"B"
首先请注意,此将 ,因为["BA", "AZ", "AY", ... "AA", "Z", "Y", "X", ..., "C", "B"]
:
"BA" > "B"
相反,您可以将反转范围转换为数组并再次反转:
("BA".."B").to_a
# => []
或者,如果您打算遍历此列表,则使用("B".."BA").to_a.reverse
会更高效:
reverse_each
答案 1 :(得分:3)
这是因为String#succ
和String#<=>
之间存在差异:
'a'.succ #=> 'b'
'a' < 'a'.succ #=> true
但:
'z'.succ #=> 'aa'
'z' < 'z'.succ #=> false
范围在生成序列时同时使用succ
和<=>
。它使用succ
生成每个连续值,并通过<=>
检查值是否确实是连续的(如果没有则结束序列)。 1
即使String#upto
也是这样的。我最近提交了bug report,因为我认为它应该正确处理这个问题。
1 这是迭代自定义对象的行为。对于内置的String
类,范围表现得更奇怪,可能是因为优化。
答案 2 :(得分:2)
Range#to_a
在内部调用其&#34;元素&#34;的方法succ
。以下是Range#to_a
class Range
def to_a
ary = []
item = self.begin
compare = self.exclude_end? ? :< : :<=
while item.public_send(compare, self.end)
ary << item
item = item.succ
end
ary
end
end
您可以尝试'A'.succ
,'B'.succ
,...查看它生成的序列。
由于'B'
按字母顺序大于'AB'
,因此循环在第一次迭代之前结束,因此您获得[]
。