Я попытался выполнить итерацию в обратном направлении, используя Range и each
:
(4..0).each do |i|
puts i
end
==> 4..0
Итерация 0..4
записывает числа. С другой диапазон , r = 4..0
кажется, хорошо, r.first == 4
, r.last == 0
.
Мне кажется странным, что приведенная выше конструкция не дает ожидаемого результата. В чем причина этого? В каких ситуациях такое поведение разумно?
.each
оператор ничего не изменил, вычисленного «результата» для возврата нет. В этом случае Ruby обычно возвращает исходный объект в случае успеха илиnil
ошибки. Это позволяет использовать подобные выражения в качестве условийif
оператора.Ответы:
Диапазон - это всего лишь что-то, что определяется его началом и концом, а не его содержимым. «Итерация» по диапазону в общем случае не имеет смысла. Рассмотрим, например, как бы вы «перебирали» диапазон, полученный двумя датами. Не могли бы вы повторить день? по месяцам? по году? по неделям? Это не совсем точно. ИМО, тот факт, что это разрешено для прямых диапазонов, следует рассматривать только как удобный метод.
Если вы хотите выполнить итерацию в обратном направлении по такому диапазону, вы всегда можете использовать
downto
:Вот еще несколько мыслей других о том, почему сложно одновременно разрешить итерацию, и постоянно иметь дело с обратными диапазонами.
источник
.each
Есть лишний,5.downto(1) { |n| puts n }
работает отлично. Кроме того, вместо всего этого r.first r.last просто сделайте(6..10).reverse_each
.= f.select :model_year, (Time.zone.now.year + 1).downto(Time.zone.now.year - 100).to_a
Как насчет того,
(0..1).reverse_each
что выполняет итерацию диапазона назад?источник
(Date.today.beginning_of_year..Date.today.yesterday).reverse_each
СпасибоИтерация по диапазону в Ruby с
each
вызовомsucc
метода для первого объекта в диапазоне.А 5 находится за пределами допустимого диапазона.
Вы можете смоделировать обратную итерацию с помощью этого хака:
Джон указал, что это не сработает, если он охватывает 0. Это будет:
Не могу сказать, что мне действительно нравятся какие-либо из них, потому что они как бы скрывают цель.
источник
Согласно книге «Programming Ruby», объект Range хранит две конечные точки диапазона и использует
.succ
член для генерации промежуточных значений. В зависимости от того, какой тип данных вы используете в своем диапазоне, вы всегда можете создать подклассInteger
и переопределить.succ
член, чтобы он действовал как обратный итератор (вы, вероятно, также захотите переопределить.next
).Вы также можете добиться желаемых результатов без использования Range. Попробуй это:
Это будет шаг от 4 до 0 с шагом -1. Однако я не знаю, сработает ли это для чего-либо, кроме целочисленных аргументов.
источник
Другой способ
(1..10).to_a.reverse
источник
Вы даже можете использовать
for
цикл:который печатает:
источник
если список не такой уж большой. Думаю,
[*0..4].reverse.each { |i| puts i }
это самый простой способ.источник
Как сказано в bta, причина в том, что
Range#each
отправляетсяsucc
в начало, затем в результат этогоsucc
вызова и так далее, пока результат не станет больше конечного значения. Вы не можете перейти от 4 к 0, коллируяsucc
, и фактически вы уже начинаете больше, чем конец.источник
Я добавляю еще одну возможность, как реализовать итерацию по обратному диапазону. Я им не пользуюсь, но это возможно. Немного рискованно исправлять объекты с рубиновым ядром.
источник
Это сработало для моего ленивого варианта использования
источник
ОП написал
а не «Можно ли это сделать?» но чтобы ответить на вопрос, который не был задан, прежде чем перейти к вопросу, который был задан на самом деле:
Поскольку заявляется, что reverse_each строит весь массив, очевидно, что метод downo будет более эффективным. Тот факт, что разработчик языка может даже подумать о реализации таких вещей, как бы связан с ответом на фактический вопрос, который задается.
Чтобы ответить на вопрос, как на самом деле ...
Причина в том, что Ruby - бесконечно удивительный язык. Некоторые сюрпризы приятны, но есть много поведения, которое совершенно нарушено. Даже если некоторые из этих следующих примеров будут исправлены в новых версиях, есть много других, и они остаются обвинительными актами в отношении мировоззрения первоначального дизайна:
приводит к "" но
приводит к
Вы, вероятно, ожидаете, что << и push будут такими же для добавления в массивы, но
Вы, вероятно, ожидаете, что 'grep' будет вести себя как его эквивалент в командной строке Unix, но он соответствует === not = ~, несмотря на свое имя.
Различные методы неожиданно являются псевдонимами друг для друга, поэтому вам придется выучить несколько имен для одного и того же объекта - например,
find
иdetect
- даже если вам нравится большинство разработчиков и вы всегда используете только одно или другое. Во многом то же самое касаетсяsize
,count
иlength
, за исключением классов, которые определяют каждый по-разному или не определяют один или два вообще.Если кто-то не реализовал что-то еще - например, основной метод
tap
был переопределен в различных библиотеках автоматизации, чтобы нажимать что-то на экране. Удачи в выяснении того, что происходит, особенно если какой-то модуль, требуемый другим модулем, обезвредил еще один модуль, чтобы сделать что-то недокументированное.Объект переменной среды, ENV, не поддерживает «слияние», поэтому вам нужно написать
В качестве бонуса вы даже можете переопределить свои или чужие константы, если передумаете, какими они должны быть.
источник
grep
каким-либо образом, формой или формой связано с тем фактом, что итерация по пустому диапазону не работает. Я также не вижу, как тот факт, что итерация по пустому диапазону является запретной операцией, каким-либо образом придает форму или форму «бесконечно удивительному» и «совершенно нарушенному».Как по мне, самый простой способ:
Другой способ перебора для перечисления:
источник