Próbowałem wykonać iterację wstecz za pomocą Range i each
:
(4..0).each do |i|
puts i
end
==> 4..0
Iteracja poprzez 0..4
zapisuje liczby. Z drugiej strony zakres r = 4..0
wydaje się być ok, r.first == 4
, r.last == 0
.
Wydaje mi się dziwne, że powyższa konstrukcja nie daje oczekiwanego rezultatu. Jaki jest tego powód? W jakich sytuacjach takie zachowanie jest uzasadnione?
.each
instrukcja niczego nie modyfikowała, nie ma obliczonego „wyniku” do zwrócenia. W takim przypadku Ruby zazwyczaj zwraca oryginalny obiekt w przypadku sukcesu inil
błędu. Umożliwia to używanie takich wyrażeń jako warunków wif
instrukcji.Odpowiedzi:
Zakres jest po prostu tym: czymś określonym przez początek i koniec, a nie przez zawartość. „Iterowanie” po zakresie nie ma sensu w ogólnym przypadku. Zastanów się na przykład, jak „iterować” zakres utworzony przez dwie daty. Czy chciałbyś iterować za dnia? według miesiąca? przez rok? za tydzień? Nie jest dobrze zdefiniowana. IMO, fakt, że jest to dozwolone dla zakresów forward, należy traktować tylko jako wygodną metodę.
Jeśli chcesz iterować wstecz w takim zakresie, zawsze możesz użyć
downto
:Oto kilka przemyśleń innych osób na temat tego, dlaczego trudno jest zarówno pozwolić na iterację, jak i konsekwentnie radzić sobie z odwrotnymi zakresami.
źródło
.each
Jest zbędny,5.downto(1) { |n| puts n }
działa bez zarzutu. Poza tym, zamiast tego wszystkiego r. Pierwszy r. Ostatni, po prostu zrób(6..10).reverse_each
.= f.select :model_year, (Time.zone.now.year + 1).downto(Time.zone.now.year - 100).to_a
A
(0..1).reverse_each
co powiesz na to, który iteruje zakres wstecz?źródło
(Date.today.beginning_of_year..Date.today.yesterday).reverse_each
Iterowanie po zakresie w Rubim za pomocą
each
wywołujesucc
metodę na pierwszym obiekcie w zakresie.A 5 jest poza zakresem.
Możesz zasymulować odwrotną iterację za pomocą tego hacka:
John zwrócił uwagę, że to nie zadziała, jeśli będzie obejmowało 0. To:
Nie mogę powiedzieć, że naprawdę lubię którekolwiek z nich, ponieważ w pewnym sensie przesłaniają intencję.
źródło
Zgodnie z książką „Programming Ruby”, obiekt Range przechowuje dwa punkty końcowe zakresu i używa elementu
.succ
członkowskiego do wygenerowania wartości pośrednich. W zależności od tego, jakiego typu danych używasz w swoim zakresie, zawsze możesz utworzyć podklasęInteger
i ponownie zdefiniować element.succ
członkowski, tak aby działał jak iterator odwrotny (prawdopodobnie chciałbyś również ponownie zdefiniować.next
).Możesz także osiągnąć oczekiwane wyniki bez korzystania z Range. Spróbuj tego:
Spowoduje to przejście od 4 do 0 w krokach co -1. Jednak nie wiem, czy to zadziała w przypadku czegokolwiek poza argumentami typu Integer.
źródło
Innym sposobem jest
(1..10).to_a.reverse
źródło
Możesz nawet użyć
for
pętli:który drukuje:
źródło
jeśli lista nie jest tak duża. myślę, że
[*0..4].reverse.each { |i| puts i }
to najprostszy sposób.źródło
Jak powiedział bta, powodem jest to, że
Range#each
wysyłasucc
na początek, następnie do wyniku tegosucc
wywołania i tak dalej, aż wynik będzie większy niż wartość końcowa. Nie możesz dostać się od 4 do 0, dzwoniącsucc
, a tak naprawdę już zaczynasz lepiej niż koniec.źródło
Dodam jeszcze jedną możliwość realizacji iteracji w odwrotnym zakresie. Nie korzystam, ale jest taka możliwość. Łatanie obiektów z rdzeniem rubinowym jest nieco ryzykowne.
źródło
To zadziałało dla mojego leniwego przypadku użycia
źródło
OP napisał
nie „Czy można to zrobić?” ale aby odpowiedzieć na pytanie, które nie zostało zadane przed przejściem do pytania, które zostało zadane:
Ponieważ twierdzi się, że reverse_each buduje całą tablicę, downto z pewnością będzie bardziej wydajne. Fakt, że projektant języka mógłby nawet rozważyć zastosowanie takich rzeczy, wiąże się z odpowiedzią na zadane pytanie.
Aby odpowiedzieć na pytanie tak, jak faktycznie zadano ...
Powodem jest to, że Ruby to nieskończenie zaskakujący język. Niektóre niespodzianki są przyjemne, ale jest wiele zachowań, które są wręcz zepsute. Nawet jeśli niektóre z poniższych przykładów zostaną poprawione w nowszych wersjach, istnieje wiele innych i pozostają one jako akty oskarżenia w myśleniu pierwotnego projektu:
daje w wyniku „” ale
prowadzi do
Prawdopodobnie spodziewałbyś się, że << i push będzie takie samo przy dołączaniu do tablic, ale
Prawdopodobnie spodziewałbyś się, że „grep” będzie zachowywał się jak jego odpowiednik w linii poleceń systemu Unix, ale mimo swojej nazwy === dopasowanie not = ~.
Różne metody są dla siebie nieoczekiwanie aliasami, więc musisz nauczyć się wielu nazw dla tej samej rzeczy - np.
find
Idetect
- nawet jeśli lubisz większość programistów i zawsze używasz tylko jednej lub drugiej. Dużo to samo dotyczysize
,count
orazlength
, z wyjątkiem klas, które określają każdy inaczej, albo nie definiują jeden lub dwa w ogóle.Chyba że ktoś zaimplementował coś innego - tak jak podstawowa metoda
tap
została na nowo zdefiniowana w różnych bibliotekach automatyzacji, aby nacisnąć coś na ekranie. Powodzenia w ustalaniu, co się dzieje, zwłaszcza jeśli jakiś moduł wymagany przez inny moduł zmusił kolejny moduł do zrobienia czegoś nieudokumentowanego.Obiekt zmiennej środowiskowej ENV nie obsługuje funkcji „scalania”, więc musisz pisać
Jako bonus możesz nawet przedefiniować swoje lub czyjeś stałe stałe, jeśli zmienisz zdanie na temat tego, jakie powinny być.
źródło
grep
jest w jakikolwiek sposób, kształt lub forma związane z faktem, że iteracja po pustym zakresie nie działa. Nie widzę też, jak fakt, że iteracja po pustym zakresie jest niczym nie działa, jest w jakikolwiek sposób „nieskończenie zaskakująca” i „wręcz zepsuta”.Jak dla mnie najprostszy sposób to:
Inny sposób na iterację w celu wyliczenia:
źródło