Différence entre «..» (double point) et «…» (triple point) dans la génération de plage?

111

Je viens de commencer à apprendre Ruby et Ruby on Rails et suis tombé sur un code de validation qui utilise des plages:

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

Au début, je pensais que la différence résidait dans l'inclusion des points de terminaison, mais dans les documents sur l'API que j'ai examinés, peu importe que ce soit le cas ..ou ...: cela incluait toujours les points de terminaison.

Cependant, j'ai fait quelques tests dans irb et cela semblait indiquer que cela ..inclut les deux points de terminaison, tout en n'incluant ...que la limite inférieure mais pas la limite supérieure. Est-ce correct?

juil
la source

Réponses:

157

La documentation de Range dit ceci:

Plages construites en utilisant la ..course du début à la fin inclusivement. Ceux créés avec ...excluent la valeur finale.

Alors a..bc'est comme a <= x <= b, alors que a...bc'est comme a <= x < b.


Notez que, alors que to_asur une plage d'entiers donne une collection d'entiers, une plage n'est pas un ensemble de valeurs, mais simplement une paire de valeurs de début / 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


Les documents utilisés pour ne pas inclure cela, à la place exigeant la lecture de la section de la pioche sur les gammes . Merci à @MarkAmery ( voir ci-dessous ) pour avoir noté cette mise à jour.

Andrew Marshall
la source
11
Exemple meilleur / moins déroutant que celui ci-dessus: (1..10).include? 10 #=> trueet(1...10).include? 10 #=> false
timmcliu
@timmcliu Bien que cela ne soit pas pertinent pour illustrer le point que (a..b) != (a...(b+1))malgré que leurs représentations de tableau soient égales (quand a, b ∈ ℤ). J'ai mis à jour ma réponse un peu pour développer cela.
Andrew Marshall
Si Range n'est pas un ensemble de valeurs, alors pourquoi ce morceau de code traite-t-il Range comme un ensemble de valeurs: (1..5) .inject {| sum, n | sum + n}
VaVa
2
@ValentinVassilev Range n'est pas un ensemble de valeurs, mais il peut les générer. injectvient de Enumerablequi Rangecomprend; Enumerableutilise #each, qui Rangemet en œuvre . La liste générée par Range#eachn'est jamais contenue dans l' Rangeobjet lui-même.
Andrew Marshall
6

C'est correct.

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 syntaxe à trois points est moins courante, mais plus agréable que (1..10-1).to_a

Chris Heald
la source
12
Je pense que c'est vraiment bizarre que plus de points signifie que la plage représente moins de valeurs. Je suppose que c'est juste que ..c'est plus courant et donc moins est préféré pour cela?
Andrew Marshall
2
@Andrew: Je pensais cela aussi, mais c'est peut-être dû au fait que la variété à deux points est plus couramment souhaitée et donc plus courte à taper?
safetycopy
1
Notez également que (a..b-1) != (a...b), bien que cette réponse implique qu'ils le sont.
Andrew Marshall
1
(a..b-1) == (a ... b) uniquement dans le cas où a et b sont des entiers et que vous énumérez les plages dans des tableaux. Considérez la plage (1,0 ... 3,5) - quelle est la valeur juste avant 3,5? Certainement pas 2.5!
Chris Heald
3

La documentation de l'API décrit maintenant ce comportement:

Plages construites en utilisant la ..course du début à la fin inclusivement. Ceux créés en utilisant...excluent la valeur finale.

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

En d'autres termes:

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
la source
1

a...b exclut la valeur finale, tandis que a..b comprend la valeur finale.

Lorsque vous travaillez avec des entiers, a...bse comporte comme 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

Mais vraiment, les plages diffèrent sur une droite numérique réelle .

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

Vous pouvez le voir lors de l'incrémentation par étapes fractionnaires.

>> (-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
la source
1
Toujours incorrect après modification. Même si a& bsont des entiers, les plages sont différentes. Ce n'est que lorsque chacun est converti en tableau qu'ils sont identiques. Un contre-exemple spécifique existe dans la réponse acceptée.
Andrew Marshall
2
@AndrewMarshall Ce que je voulais dire avec cet exemple (mais pas très bien évidemment) est sur une échelle entière, il se comporte de cette façon. Ce n'est pas le cas sur une échelle fractionnaire plus précise, comme indiqué dans votre réponse. Je pense que les plages sont le plus souvent utilisées sur une échelle entière, c'est pourquoi je pense qu'une telle explication est utile.
Dennis du
-4

.. et ... désignent une plage.

Il suffit de le voir dans 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
la source
2
Cela ne répond pas vraiment à la question, cependant; les deux sont décrits comme des plages. Gamme inclusive vs exclusive .
Craig Ringer