Różnica między „..” (podwójna kropka) a „…” (potrójna kropka) w generowaniu zakresu?

111

Właśnie zacząłem uczyć się Ruby i Ruby on Rails i natknąłem się na kod walidacyjny, który używa zakresów:

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

Na początku myślałem, że różnica polega na włączeniu punktów końcowych, ale w dokumentacji API, którą sprawdziłem, nie miało znaczenia, czy tak jest, ..czy ...: zawsze zawierał punkty końcowe.

Jednak przeprowadziłem kilka testów w irb i wydawało się, że wskazuje to na ..oba punkty końcowe, podczas gdy ...tylko dolną granicę, ale nie górną. Czy to jest poprawne?

juil
źródło

Odpowiedzi:

157

Dokumentacji Zakres mówi w ten sposób:

Zakresy skonstruowane przy użyciu ..run od początku do końca włącznie. Te utworzone za pomocą ...wykluczają wartość końcową.

Więc a..bjest jak a <= x <= b, a a...bjest jak a <= x < b.


Zauważ, że podczas gdy to_aw Range of integers daje zbiór liczb całkowitych, Range nie jest zbiorem wartości, ale po prostu parą wartości początkowych / końcowych:

(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


Dokumenty wcześniej tego nie zawierały, zamiast tego wymagały przeczytania sekcji kilofa na temat zasięgu . Dzięki @MarkAmery ( patrz poniżej ) za odnotowanie tej aktualizacji.

Andrew Marshall
źródło
11
Lepszy / mniej zagmatwany przykład niż powyższy: (1..10).include? 10 #=> truei(1...10).include? 10 #=> false
timmcliu
@timmcliu Chociaż nie ma to znaczenia dla zilustrowania punktu, który (a..b) != (a...(b+1))pomimo tego, że ich reprezentacje tablicowe są równe (gdy a, b ∈ ℤ). Zaktualizowałem nieco moją odpowiedź, aby to rozwinąć.
Andrew Marshall,
Jeśli Range nie jest zbiorem wartości, to dlaczego ten fragment kodu traktuje Range jako zbiór wartości: (1..5) .inject {| sum, n | sum + n}
VaVa
2
Zakres @ValentinVassilev nie jest zbiorem wartości, ale może je wygenerować. injectpochodzi z Enumerablektórego Rangeobejmuje; Enumerablewykorzystuje #each, który Rangeimplementuje . Lista wygenerowana przez Range#eachnigdy nie jest zawarta w samym Rangeobiekcie.
Andrew Marshall,
6

To jest poprawne.

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]

Składnia z trzema kropkami jest mniej powszechna, ale jest ładniejsza niż (1..10-1).to_a

Chris Heald
źródło
12
Myślę, że to naprawdę dziwne, że więcej kropek oznacza, że ​​zakres reprezentuje mniej wartości. Myślę, że to po prostu ..jest bardziej powszechne i dlatego mniej jest dla niego preferowane?
Andrew Marshall,
2
@Andrew: Też tak myślałem, ale może to dlatego, że odmiana z dwiema kropkami jest częściej pożądana, a przez to krótsza do pisania?
safetycopy
1
Zauważ też, że (a..b-1) != (a...b)chociaż ta odpowiedź sugeruje, że tak jest.
Andrew Marshall,
1
(a..b-1) == (a ... b) tylko w przypadku, gdy a i b są liczbami całkowitymi i wyliczysz zakresy w tablice. Rozważ zakres (1,0 ... 3,5) - jaka jest wartość tuż przed 3,5? Na pewno nie 2.5!
Chris Heald
3

Dokumentacja API opisuje teraz to zachowanie:

Zakresy skonstruowane przy użyciu ..run od początku do końca włącznie. Te utworzone za pomocą ...wykluczają wartość końcową.

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

Innymi słowy:

2.1.3 :001 > ('a'...'d').to_a
 => ["a", "b", "c"] 
2.1.3 :002 > ('a'..'d').to_a
 => ["a", "b", "c", "d"] 
Mark Amery
źródło
1

a...b wyklucza wartość końcową, a a..b zawiera wartość końcową.

Podczas pracy z liczbami całkowitymi a...bzachowuje się jak 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

Ale tak naprawdę zakresy różnią się na osi liczb rzeczywistych .

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

Możesz to zobaczyć, zwiększając w krokach ułamkowych.

>> (-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]
Dennis
źródło
1
Nadal niepoprawne po edycji. Nawet jeśli a& bsą liczbami całkowitymi, zakresy są różne. Tylko wtedy, gdy każdy jest konwertowany na tablicę, są takie same. W zaakceptowanej odpowiedzi istnieje konkretny kontrprzykład.
Andrew Marshall,
2
@AndrewMarshall To, co chciałem powiedzieć w tym przykładzie (ale niezbyt dobrze ewidentnie) jest w skali całkowitej, zachowuje się w ten sposób. Tak nie jest w przypadku bardziej precyzyjnej skali ułamkowej, jak wskazano w Twojej odpowiedzi. Wydaje mi się, że zakresy są najczęściej używane w skali całkowitej, dlatego uważam, że takie wyjaśnienie jest pomocne.
Dennis,
-4

.. i ... oznaczają zakres.

Po prostu zobacz to w 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
daniel
źródło
2
Jednak tak naprawdę nie odpowiada na pytanie; oba są opisane jako zakresy. Gama Inclusive vs Exclusive .
Craig Ringer