Почему (1..5).each
выполняет итерацию по 1,2,3,4,5
, но (5..1)
не будет? Вместо этого он возвращает диапазон.
1.9.2p290 :007 > (1..5).each do |i| puts i end
1
2
3
4
5
=> 1..5
1.9.2p290 :008 > (5..1).each do |i| puts i end
=> 5..1
Почему (1..5).each
выполняет итерацию по 1,2,3,4,5
, но (5..1)
не будет? Вместо этого он возвращает диапазон.
1.9.2p290 :007 > (1..5).each do |i| puts i end
1
2
3
4
5
=> 1..5
1.9.2p290 :008 > (5..1).each do |i| puts i end
=> 5..1
Самый простой способ сделать это - использовать downto
5.downto(1) do |i| puts i end
Диапазоны используют <=>
, чтобы определить, закончилась ли итерация; 5 <=> 1 == 1
(больше, чем), поэтому оно выполняется до его начала. Даже если они этого не сделали, диапазон повторяется с использованием succ
; 5.succ
6
, все еще не повезло. Диапазон step
не может быть отрицательным, поэтому он не будет работать.
Он возвращает диапазон, потому что each
возвращает то, на что он был вызван. Используйте downto
, если это сама функциональность, которую вы ищете, иначе приведенный выше ответ на ваш фактический вопрос о "почему".
Вы можете легко расширить класс Range, в частности каждый метод, чтобы он совместим как с восходящими, так и с нисходящими диапазонами:
class Range
def each
if self.first < self.last
self.to_s=~(/\.\.\./) ? last = self.last-1 : last = self.last
self.first.upto(last) { |i| yield i}
else
self.to_s=~(/\.\.\./) ? last = self.last+1 : last = self.last
self.first.downto(last) { |i| yield i }
end
end
end
Затем следующий код будет работать так, как вы ожидали:
(0..10).each { |i| puts i}
(0...10).each { |i| puts i}
(10..0).each { |i| puts i}
(10...0).each { |i| puts i}
Это даже не имеет никакого отношения к Ruby, это просто простая математическая модель: диапазон, начинающийся с 5
и заканчивающийся на 1
, пуст. Итерации нет.
Потому что Ruby делает только то, что он сказал, а не то, что вы означают.
Невозможно определить, хотите ли вы перейти в обратном направлении (т.е. 5, 4, 3, 2, 1) или хотите ли вы только цифры, начинающиеся с 5, которые меньше или равны 1. Теоретически возможно, что кто-то может захотеть последнего, и потому, что Ruby не может сказать, чего вы действительно хотите, он пойдет с последним.