Perbedaan antara '..' (double-dot) dan '…' (triple-dot) dalam generasi jangkauan?

111

Saya baru saja mulai mempelajari Ruby dan Ruby on Rails dan menemukan kode validasi yang menggunakan rentang:

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

Awalnya saya pikir perbedaannya ada pada penyertaan titik akhir, tetapi dalam dokumen API yang saya lihat, tampaknya tidak masalah apakah itu ..atau ...: itu selalu menyertakan titik akhir.

Namun, saya melakukan beberapa pengujian di irb dan tampaknya menunjukkan bahwa itu ..mencakup kedua titik akhir, sementara ...hanya menyertakan batas bawah tetapi tidak di atas. Apakah ini benar?

juil
sumber

Jawaban:

157

The dokumentasi untuk Rentang mengatakan ini:

Rentang dibangun menggunakan ..run dari awal hingga akhir secara inklusif. Yang dibuat menggunakan ...mengecualikan nilai akhir.

Jadi a..bseperti a <= x <= b, sedangkan a...bseperti a <= x < b.


Perhatikan bahwa, saat to_aRange bilangan bulat memberikan kumpulan bilangan bulat, Range bukanlah sekumpulan nilai, tetapi hanya sepasang nilai awal / akhir:

(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


Dokumen biasanya tidak menyertakan ini, malah meminta membaca bagian beliung di Rentang . Terima kasih kepada @MarkAmery ( lihat di bawah ) untuk mencatat pembaruan ini.

Andrew Marshall
sumber
11
Contoh yang lebih baik / tidak membingungkan dari yang di atas: (1..10).include? 10 #=> truedan(1...10).include? 10 #=> false
timmcliu
@timmcliu Meskipun tidak relevan untuk menggambarkan titik (a..b) != (a...(b+1))meskipun representasi array mereka sama (bila a, b ∈ ℤ). Saya telah memperbarui jawaban saya sedikit untuk memperluasnya.
Andrew Marshall
Jika Range bukan sekumpulan nilai, lalu mengapa potongan kode ini memperlakukan Range sebagai satu set nilai: (1..5) .inject {| sum, n | sum + n}
VaVa
2
@ValentinVassilev Range bukanlah sekumpulan nilai, tetapi dapat menghasilkannya. injectberasal dari Enumerableyang Rangemeliputi; Enumerablememanfaatkan #each, yang Rangemengimplementasikan . Daftar yang dibuat oleh Range#eachtidak pernah berada di dalam Rangeobjek itu sendiri.
Andrew Marshall
6

Itu betul.

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]

Sintaks tiga titik kurang umum, tetapi lebih bagus dari (1..10-1).to_a

Chris Heald
sumber
12
Saya pikir sangat aneh bahwa lebih banyak titik berarti kisaran mewakili nilai yang lebih sedikit . Saya kira itu hanya yang ..lebih umum dan dengan demikian kurang disukai untuk itu?
Andrew Marshall
2
@ Andrew: Saya juga berpikir begitu, tapi mungkin itu tergantung pada variasi dua titik yang lebih umum diinginkan dan karenanya lebih pendek untuk diketik?
safetycopy
1
Juga, perhatikan bahwa (a..b-1) != (a...b), meskipun jawaban ini menyiratkan demikian.
Andrew Marshall
1
(a..b-1) == (a ... b) hanya dalam kasus di mana a dan b adalah integer dan Anda menghitung rentang menjadi array. Pertimbangkan kisaran (1.0 ... 3.5) - berapa nilai sebelum 3.5? Tentu bukan 2.5!
Chris Heald
3

Dokumen API sekarang menjelaskan perilaku ini:

Rentang dibangun menggunakan ..run dari awal hingga akhir secara inklusif. Yang dibuat menggunakan ...mengecualikan nilai akhir.

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

Dengan kata lain:

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
sumber
1

a...b tidak termasuk nilai akhir, sementara a..b memasukkan nilai akhir.

Saat bekerja dengan bilangan bulat, a...bberperilaku sebagai 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

Tapi sebenarnya rentangnya berbeda pada garis bilangan real .

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

Anda dapat melihat ini saat menaikkan dalam langkah pecahan.

>> (-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
sumber
1
Masih salah setelah diedit. Meskipun a& badalah bilangan bulat, rentangnya berbeda. Hanya ketika masing-masing diubah menjadi array mereka sama. Contoh balasan tertentu ada dalam jawaban yang diterima.
Andrew Marshall
2
@AndrewMarshall Apa yang ingin saya katakan dengan contoh itu (tetapi tidak terlalu jelas) berada pada skala integer yang berperilaku seperti itu. Ini tidak terjadi pada skala pecahan yang lebih tepat, seperti yang ditunjukkan dalam jawaban Anda. Saya rasa rentang paling sering digunakan pada skala integer, itulah sebabnya saya yakin penjelasan seperti itu sangat membantu.
Dennis
-4

.. dan ... menunjukkan rentang.

Lihat saja di 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
sumber
2
Tidak benar-benar menjawab pertanyaan itu; keduanya digambarkan sebagai rentang. Rentang Inklusif vs Eksklusif .
Craig Ringer