¿Diferencia entre '..' (doble punto) y '…' (triple punto) en la generación de rango?

111

Acabo de comenzar a aprender Ruby y Ruby on Rails y encontré un código de validación que usa rangos:

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

Al principio pensé que la diferencia estaba en la inclusión de puntos finales, pero en los documentos de API que miré, no parecía importar si era ..o ...: siempre incluía los puntos finales.

Sin embargo, hice algunas pruebas en irb y pareció indicar que ..incluye ambos puntos finales, mientras que ...solo incluía el límite inferior pero no el superior. ¿Es esto correcto?

juil
fuente

Respuestas:

157

La documentación de Range dice esto:

Los rangos construidos usando ..corren desde el principio hasta el final inclusive. Los creados con ...excluyen el valor final.

Así a..bes como a <= x <= b, mientras que a...bes como a <= x < b.


Tenga en cuenta que, mientras que to_aun rango de números enteros da una colección de números enteros, un rango no es un conjunto de valores, sino simplemente un par de valores de inicio / fin:

(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


Los documentos solían no incluir esto, en su lugar requerían leer la sección de Pico sobre rangos . Gracias a @MarkAmery ( ver más abajo ) por señalar esta actualización.

Andrew Marshall
fuente
11
Ejemplo mejor / menos confuso que el anterior: (1..10).include? 10 #=> truey(1...10).include? 10 #=> false
timmcliu
@timmcliu Aunque no es relevante para ilustrar el punto que a (a..b) != (a...(b+1))pesar de que sus representaciones de matriz son iguales (cuando a, b ∈ ℤ). Actualicé un poco mi respuesta para ampliar eso.
Andrew Marshall
Si Range no es un conjunto de valores, entonces ¿por qué este código trata a Range como un conjunto de valores: (1..5) .inject {| sum, n | sum + n}
VaVa
2
@ValentinVassilev Range no es un conjunto de valores, pero puede generarlos. injectviene de lo Enumerableque Rangeincluye; Enumerableutiliza #each, que Rangeimplementa . La lista generada por Range#eachnunca está contenida dentro del Rangeobjeto en sí.
Andrew Marshall
6

Eso es correcto.

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]

La sintaxis de tres puntos es menos común, pero es más agradable que (1..10-1).to_a

Chris Heald
fuente
12
Creo que es realmente extraño que más puntos signifique que el rango representa menos valores. Supongo que es solo que ..es más común y, por lo tanto, se prefiere menos para ello.
Andrew Marshall
2
@ Andrew: Yo también pensé eso, pero tal vez se deba a que la variedad de dos puntos es la más deseada y, por lo tanto, más corta de escribir.
copia de seguridad
1
Además, tenga en cuenta que (a..b-1) != (a...b), aunque esta respuesta implica que lo son.
Andrew Marshall
1
(a..b-1) == (a ... b) solo en el caso donde ayb son números enteros y usted enumera los rangos en matrices. Considere el rango (1.0 ... 3.5): ¿cuál es el valor justo antes de 3.5? ¡Ciertamente no 2.5!
Chris Heald
3

Los documentos de la API ahora describen este comportamiento:

Los rangos construidos usando ..corren desde el principio hasta el final inclusive. Los creados con ...excluyen el valor final.

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

En otras palabras:

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

a...b excluye el valor final, mientras que a..b incluye el valor final.

Cuando se trabaja con números enteros, se a...bcomporta como 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

Pero realmente los rangos difieren en una recta numérica real .

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

Puede ver esto al incrementar en pasos fraccionarios.

>> (-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
fuente
1
Sigue siendo incorrecto después de la edición. Incluso si a& bson números enteros, los rangos son diferentes. Solo cuando cada uno se convierte en una matriz son iguales. Existe un contraejemplo específico en la respuesta aceptada.
Andrew Marshall
2
@AndrewMarshall Lo que quise decir con ese ejemplo (pero no muy bien, evidentemente) está en una escala entera, se comporta de esa manera. Este no es el caso en una escala fraccionaria más precisa, como se señala en su respuesta. Sin embargo, creo que los rangos se usan con mayor frecuencia en una escala de números enteros, por lo que creo que esa explicación es útil.
Dennis
-4

.. y ... denotan un rango.

Solo míralo en 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
fuente
2
Sin embargo, no responde realmente a la pregunta; ambos se describen como rangos. Gama inclusiva vs exclusiva .
Craig Ringer