Differenza tra ".." (doppio punto) e "..." (triplo punto) nella generazione dell'intervallo?


111

Ho appena iniziato a imparare Ruby e Ruby on Rails e mi sono imbattuto in codice di convalida che utilizza intervalli:

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

All'inizio pensavo che la differenza fosse nell'inclusione degli endpoint, ma nei documenti API che ho esaminato, non sembrava importare se fosse ..o ...: includeva sempre gli endpoint.

Tuttavia, ho eseguito alcuni test in irb e sembrava indicare che ..include entrambi gli endpoint, mentre ...includeva solo il limite inferiore ma non quello superiore. È corretto?

Risposte:


157

La documentazione per Range dice questo:

Intervalli costruiti utilizzando la ..corsa dall'inizio alla fine in modo inclusivo. Quelli creati utilizzando ...escludono il valore finale.

Così a..bè come a <= x <= b, mentre a...bè come a <= x < b.


Nota che, mentre to_asu un intervallo di interi fornisce una raccolta di numeri interi, un intervallo non è un insieme di valori, ma semplicemente una coppia di valori di inizio / fine:

(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


I documenti non lo includevano, richiedendo invece la lettura della sezione del piccone sui Ranges . Grazie a @MarkAmery ( vedi sotto ) per aver notato questo aggiornamento.


11
Esempio migliore / meno confuso del precedente: (1..10).include? 10 #=> truee(1...10).include? 10 #=> false
timmcliu

@timmcliu Sebbene non sia rilevante per illustrare il punto che (a..b) != (a...(b+1))nonostante le loro rappresentazioni di array siano uguali (quando a, b ∈ ℤ). Ho aggiornato un po 'la mia risposta per ampliarlo.
Andrew Marshall

Se Range non è un insieme di valori, allora perché questo pezzo di codice tratta Range come un insieme di valori: (1..5) .inject {| sum, n | sum + n}
VaVa

2
@ValentinVassilev Range non è un insieme di valori, ma può generarli. injectproviene da Enumerablecui Rangeinclude; Enumerableutilizza #each, che Rangeimplementa . L'elenco generato da Range#eachnon è mai contenuto all'interno Rangedell'oggetto stesso.
Andrew Marshall

6

È corretto.

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 sintassi a tre punti è meno comune, ma è più gradevole di (1..10-1).to_a


12
Penso che sia davvero bizzarro che più punti significano che l'intervallo rappresenta meno valori. Immagino sia solo che ..è più comune e quindi meno è preferito per questo?
Andrew Marshall

2
@ Andrew: lo pensavo anch'io, ma forse dipende dal fatto che la varietà a due punti è più comunemente desiderata e quindi più breve da digitare?
safetycopy

1
Inoltre, nota che (a..b-1) != (a...b), sebbene questa risposta implichi che lo sono.
Andrew Marshall

1
(a..b-1) == (a ... b) solo nel caso in cui aeb sono numeri interi e si enumerano gli intervalli in array. Considera l'intervallo (1,0 ... 3,5): qual è il valore appena prima di 3,5? Certamente non 2,5!
Chris Heald

3

I documenti dell'API ora descrivono questo comportamento:

Intervalli costruiti utilizzando la ..corsa dall'inizio alla fine in modo inclusivo. Quelli creati utilizzando ...escludono il valore finale.

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

In altre parole:

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 esclude il valore finale, mentre a..b include il valore finale.

Quando si lavora con numeri interi, a...bsi comporta come 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

Ma in realtà gli intervalli differiscono su una linea di numeri reali .

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

Puoi vederlo quando si incrementa in passaggi frazionari.

>> (-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
Ancora errato dopo la modifica. Anche se a& bsono numeri interi, gli intervalli sono diversi. Solo quando ciascuno viene convertito in un array sono uguali. Un controesempio specifico esiste nella risposta accettata.
Andrew Marshall

2
@AndrewMarshall Quello che volevo dire con quell'esempio (ma evidentemente non molto bene) è che su scala intera si comporta in quel modo. Questo non è il caso su una scala frazionaria più precisa, come sottolineato nella tua risposta. Tuttavia, ritengo che gli intervalli siano usati più spesso su una scala intera, motivo per cui credo che tale spiegazione sia utile.
Dennis

-4

.. e ... denotano un intervallo.

Basta vederlo in 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
Tuttavia, non risponde veramente alla domanda; entrambi sono descritti come intervalli. Gamma inclusiva vs esclusiva .
Craig Ringer
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.