Разница между «..» (двойная точка) и «…» (тройная точка) в генерации диапазона?

111

Я только начал изучать Ruby и Ruby on Rails и наткнулся на код проверки, который использует диапазоны:

validates_inclusion_of :age, :in => 21..99
validates_exclusion_of :age, :in => 0...21, :message => "Sorry, you must be over 21"

Сначала я подумал, что разница заключается во включении конечных точек, но в документации по API, которую я изучал, не имело значения, было ли это ..или ...: конечные точки всегда включались.

Тем не менее, я провел некоторое тестирование в irb, и мне показалось, что он ..включает обе конечные точки, но ...включал только нижнюю границу, но не верхнюю. Это верно?

июль
источник

Ответы:

157

В документации для Range сказано следующее:

Диапазоны, построенные с использованием ..прогона от начала до конца включительно. Те, которые созданы с использованием, ...исключают конечное значение.

Так a..bнравится a <= x <= b, а a...bвот как a <= x < b.


Обратите внимание, что, в то время to_aкак диапазон целых чисел дает набор целых чисел, диапазон не является набором значений, а просто парой начальных / конечных значений:

(1..5).include?(5)           #=> true
(1...5).include?(5)          #=> false

(1..4).include?(4.1)         #=> false
(1...5).include?(4.1)        #=> true
(1..4).to_a == (1...5).to_a  #=> true
(1..4) == (1...5)            #=> false


Документы раньше не включали это, вместо этого требовалось прочитать раздел Кирки о диапазонах . Спасибо @MarkAmery ( см. Ниже ) за то, что отметили это обновление.

Эндрю Маршалл
источник
11
Лучший / менее запутанный пример, чем приведенный выше: (1..10).include? 10 #=> trueи(1...10).include? 10 #=> false
timmcliu
@timmcliu Хотя это не имеет отношения к иллюстрации того, что, (a..b) != (a...(b+1))несмотря на то, что их представления массива равны (когда a, b ∈ ℤ). Я немного обновил свой ответ, чтобы подробнее остановиться на этом.
Эндрю Маршалл,
Если Range не является набором значений, то почему этот фрагмент кода обрабатывает Range как набор значений: (1..5) .inject {| sum, n | sum + n}
VaVa
2
@ValentinVassilev Range - это не набор значений, но он может их генерировать. injectпроисходит из Enumerableкоторого Rangeвключает; Enumerableиспользует #each, который Rangeорудует . Список, созданный с помощью Range#each, никогда не содержится в самом Rangeобъекте.
Эндрю Маршалл
6

Это верно.

1.9.3p0 :005 > (1...10).to_a
 => [1, 2, 3, 4, 5, 6, 7, 8, 9]
1.9.3p0 :006 > (1..10).to_a
 => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Синтаксис с тремя точками встречается реже, но он лучше, чем (1..10-1).to_a

Крис Хилд
источник
12
Я думаю, что это действительно странно, что больше точек означает, что диапазон представляет меньше значений. Я думаю, это просто ..более распространено, и поэтому для него предпочтительнее меньше?
Эндрю Маршалл,
2
@Andrew: Я тоже так думал, но, может быть, дело в том, что двухточечное разнообразие более востребовано и, следовательно, короче для ввода?
Safetycopy
1
Также обратите внимание, что (a..b-1) != (a...b), хотя этот ответ подразумевает, что это так.
Эндрю Маршалл,
1
(a..b-1) == (a ... b) только в том случае, если a и b являются целыми числами и вы перечисляете диапазоны в массивы. Рассмотрим диапазон (1,0 ... 3,5) - какое значение непосредственно перед 3,5? Уж точно не 2,5!
Крис Хилд,
3

Документы API теперь описывают это поведение:

Диапазоны, построенные с использованием ..прогона от начала до конца включительно. Те, которые созданы с использованием, ...исключают конечное значение.

- http://ruby-doc.org/core-2.1.3/Range.html

Другими словами:

2.1.3 :001 > ('a'...'d').to_a
 => ["a", "b", "c"] 
2.1.3 :002 > ('a'..'d').to_a
 => ["a", "b", "c", "d"] 
Марк Эмери
источник
1

a...b исключает конечное значение, а a..b включает конечное значение.

При работе с целыми числами a...bведет себя как a..b-1.

>> (-1...3).to_a
=> [-1, 0, 1, 2]

>> (-1..2).to_a
=> [-1, 0, 1, 2]

>> (-1..2).to_a == (-1...3).to_a
=> true

Но на самом деле диапазоны различаются на числовой прямой .

>> (-1..2) == (-1...3)
=> false

Вы можете увидеть это при дробном увеличении.

>> (-1..2).step(0.5).to_a
=> [-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0]

>> (-1...3).step(0.5).to_a
=> [-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5]
Деннис
источник
1
Все еще неверно после редактирования. Даже если a& b- целые числа, диапазоны разные. Только когда каждый из них преобразован в массив, они становятся одинаковыми. В принятом ответе есть конкретный контрпример.
Эндрю Маршалл
2
@AndrewMarshall То, что я хотел сказать этим примером (но, очевидно, не очень хорошо), находится в целочисленном масштабе, он ведет себя таким образом. Это не относится к более точной дробной шкале, как указано в вашем ответе. Я считаю, что диапазоны чаще всего используются в целочисленной шкале, поэтому я считаю, что такое объяснение полезно.
Деннис
-4

.. и ... обозначают диапазон.

Просто посмотрите это в irb:

ruby-1.9.2-p290 :032 > (1...2).each do puts "p" end
p
 => 1...2 
ruby-1.9.2-p290 :033 > (1..2).each do puts "p" end
p
p
Дэниел
источник
2
Однако не совсем отвечает на вопрос; оба описаны как диапазоны. Инклюзивный и эксклюзивный ассортимент.
Craig Ringer