2010-10-01 13 views
7

Sto imparando Ruby e ho appena acquisito alcune informazioni su array e intervalli. Mi sono imbattuto in qualcosa sulle fette che, mentre a prima vista ha un senso, mi confonde un po 'quando ci guardo più a fondo.Ruby: L'intervallo è vuoto, ma l'affettatura produce elementi

IRB afferma che (2..-1).to_a è un array vuoto, ovvero nessun valore nell'intervallo, giusto?
Ma se uso lo stesso intervallo in [:a, :b, :c, :d, :e][2..-1], torno [:c, :d, :e] anziché un array vuoto.

Ora, sono a conoscenza del fatto che -1 rappresenta l'ultimo elemento dell'array, quindi ha senso che ciò che è stato scelto, sia stato fatto. Ma se l'intervallo stesso fosse vuoto, come viene selezionato qualcosa?

risposta

11

Questa è una domanda affascinante. La risposta è che non sono i singoli elementi dell'intervallo che vengono ispezionati quando si affetta l'array, ma gli elementi first e last. In particolare:

>> (2..-1).to_a 
=> [] 
>> (2..-1).first 
=> 2 
>> (2..-1).last 
=> -1 

Così l'esempio funziona, poiché affetta la matrice dall'elemento [2] all'elemento [-1].

Se si desidera un modo coerente per pensare a questo, ritengono che (2..-1).to_a uscite gli interi riscontrate tra 2 e -1 (di cui non ce ne sono), ma che [2..-1] significa dall'indice 2 al -1dell'indice .

(Fonte: array.c e range.c nella sorgente di Ruby.)

E, la parte bonus complicata: per ottenere il significato stavi pensando, si potrebbe usare

>> [:a, :b, :c, :d, :e].values_at *(2..-1).to_a 
=> [] 
+0

Per riferimento futuro, qual è il stella in '* (2 ..- 1) .to_a' per? – cHao

+0

che sarebbe l'operatore "splat". – Peter

+2

Più precisamente: 'pippo ([1,2,3])' chiama 'pippo' con un argomento (lista). 'foo (* [1,2,3])' lo chiama con tre: è equivalente a 'foo (1,2,3)'; la lista viene decostruita e i suoi elementi schizzano sulla chiamata di funzione. – Amadan

3

Nell'operazione di affettamento non si vede l'intervallo come Range di per sé, è solo guardando i valori degli endpoint, in questo caso 2 e -1. Poiché -1 ha un significato speciale (vale a dire l'ultimo elemento nell'elenco) restituisce semplicemente tutto dall'elemento nell'indice 2 alla fine dell'elenco. Non pensare a questo come a Range qui, basti pensare che è un modo conveniente per far passare due numeri (esprimendo due punti finali).

1

Il metodo Array#[] fa non utilizzare l'intervallo come intervallo (ovvero, non chiama include? o qualcosa del genere); prende solo i due numeri da esso e controlla se è esclusivo.

si può immaginare di guardare un po 'come questo (anche se il vero [] è implementato in C, non in Ruby, e, naturalmente, si occupa anche argomenti diversi intervalli):

def [](range) 
    start = range.begin 
    length = range.end - start 
    length -= 1 if range.exclude_end? 
    slice(start, length) 
end 
Problemi correlati