Ruby: диапазон пуст, но разрезание с ним производит элементы - PullRequest
7 голосов
/ 01 октября 2010

Я изучаю Ruby и только что разбираюсь с массивами и диапазонами. Я натолкнулся на кое-что о ломтиках, которое, хотя на первый взгляд имеет смысл, немного смущает меня, когда я смотрю в него глубже.

IRB говорит о том, что (2..-1).to_a - пустой массив, что означает отсутствие значений в диапазоне, верно?
Но если я использую тот же диапазон в [:a, :b, :c, :d, :e][2..-1], я получаю обратно [:c, :d, :e], а не пустой массив.

Теперь, я знаю, что -1 представляет последний элемент массива, так что имеет смысл, что то, что было выбрано, было сделано. Но если сам диапазон будет пустым, как он будет выбирать что-либо?

Ответы [ 3 ]

11 голосов
/ 01 октября 2010

Это увлекательный вопрос. Ответ заключается в том, что при разрезании массива проверяются не отдельные элементы диапазона, а элементы first и last. В частности:

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

Таким образом, пример работает, так как он разрезает массив от элемента [2] до элемента [-1].

Если вам нужен последовательный способ думать об этом, учтите, что (2..-1).to_a выводит целые числа, найденные между 2 и -1 (которых нет ни одного), но что [2..-1] означает из 2 index to -1 index .

(Источник: array.c и range.c в источнике Ruby.)

И, сложная бонусная часть: чтобы понять смысл, о котором вы думали, вы можете использовать

>> [:a, :b, :c, :d, :e].values_at *(2..-1).to_a
=> []
3 голосов
/ 01 октября 2010

В операции среза он не видит диапазон как Range как таковой, он просто смотрит на значения конечных точек, в данном случае 2 и -1. Поскольку -1 имеет особое значение (то есть последний элемент в списке), он просто возвращает все, начиная от элемента с индексом 2 и заканчивая концом списка. Не думайте об этом как о Range здесь, просто подумайте об этом как об удобном способе передачи двух чисел (выражающих две конечные точки).

1 голос
/ 01 октября 2010

Метод Array#[] не использует диапазон как диапазон (то есть он не вызывает include? для него или чего-либо подобного);он просто извлекает из него два числа и проверяет, является ли он эксклюзивным.

Вы можете себе представить, что это выглядит примерно так (хотя настоящее [] реализовано в C, а не в Ruby и, конечно, также обрабатывает аргументы, отличные от диапазонов):

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