为什么Range对象与A' ..' AB'之间存在差异。和' B' AB' AB'在Ruby?

时间:2017-06-23 09:00:21

标签: ruby string range

有人可以解释一下为什么

('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
#=> []

3 个答案:

答案 0 :(得分:3)

string1..string2会列出"所有"两个值之间的字符串 in"字典顺序"

更确切地说,它会在string1上重复调用String#succ,直到达到string2

"A".succ == "B"
"B".succ == "C"
# ...
"Z".succ == "AA"
"AA".succ == "AB"

另一方面,如果string2 < string1(即&#34; string2string1之前出现在字典&#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#succString#<=>之间存在差异:

'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',因此循环在第一次迭代之前结束,因此您获得[]