2010-10-01 6 views
7

Estoy aprendiendo sobre Ruby y acabo de entrar en algunas cosas sobre arreglos y rangos. Me he topado con algo sobre rebanadas que, si bien tiene sentido a primera vista, me confunde un poco cuando lo miro más profundamente.Ruby: El rango está vacío, pero cortarlo produce elementos

IRB dice que (2..-1).to_a es una matriz vacía, lo que significa que no hay valores en el rango, ¿verdad?
Pero si utilizo el mismo rango en [:a, :b, :c, :d, :e][2..-1], obtengo [:c, :d, :e] en lugar de una matriz vacía.

Ahora, soy consciente de que -1 representa el último elemento de la matriz, por lo que tiene sentido que lo que se seleccionó, lo hizo. Pero si el rango en sí estaría vacío, ¿cómo está seleccionando algo?

Respuesta

11

Esta es una pregunta fascinante. La respuesta es que no son los elementos individuales del rango los que se inspeccionan al cortar el conjunto, sino los elementos first y last. Específicamente:

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

Así funciona el ejemplo, ya que rebana la matriz desde el elemento [2] al elemento [-1].

Si desea una forma coherente de pensar en esto, consideran que (2..-1).to_a da salida a los enteros que se encuentran entre 2 y -1 (de los cuales hay ninguno), pero que [2..-1] significa que a partir del índice 2 a la -1índice .

(Fuente: array.c y range.c en la fuente de Ruby.)

Y, la parte de bonificación complicada: para obtener el significado que estabas pensando, podría utilizar

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

Para referencia futura, ¿cuál es la estrella en '* (2 ..- 1) .to_a' para? – cHao

+0

que sería el operador 'splat'. – Peter

+2

Más precisamente: 'foo ([1,2,3])' llama a 'foo' con un argumento (de lista). 'foo (* [1,2,3])' lo llama con tres: es equivalente a 'foo (1,2,3)'; la lista se deconstruye y sus elementos se salpican sobre la llamada de función. – Amadan

3

En la operación de corte no está viendo el rango como Range per se, solo está mirando los valores de los puntos finales, en este caso 2 y . Dado que -1 tiene un significado especial (es decir, el último elemento de la lista) simplemente devuelve todo, desde el elemento en el índice 2 hasta el final de la lista. No lo considere como Range aquí, solo piense en ello como una manera conveniente de pasar dos números (expresando dos puntos finales).

1

El método Array#[] hace no use el rango como un rango (es decir, no llama al include? o algo por el estilo); simplemente quita los dos números y comprueba si es exclusivo.

Se puede imaginar que se vea algo como esto (aunque el verdadero [] se implementa en C, no en Ruby, y por supuesto también se ocupa de argumentos distintos rangos):

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