Chodzi o coś bardzo pragmatycznego. Kiedy chcesz przeiterować jakąś kolekcję to interesują Cię kolejne jej elementy. Dbanie o zmienną indeksacyjną wprowadza niepotrzebny bałagan. Poza tym nie wszystkie kolekcje (iteratory) muszą mieć dostęp po indeksie liczbowym (hashe, pliki). Nawet w javie dodano taki “for-each” (http://leepoint.net/notes-java/flow/loops/foreach.html) właśnie po to, żeby nie trzeba było ręcznie dbać o indeks. Mają przyzwyczajenia z takich języków jak C/C++ to może się wydawać wręcz idiotyczne, małostkowe, ale to tylko wrażenie.
Ja oczywiście zgadzam się, że taki kod z iteratorem jest bardziej czytelny. Interesowało mnie tylko czy jest tu coś głębszego, czy tylko o czytelność kodu chodzi. I chyba odpowiedź już dostałem
Co do “głębszego” – jeśli będziesz tworzył własną klasę, która będzie Enumerable (czyli po prostu kolekcję), warto poczytać przynajmniej wstęp: http://ruby-doc.org/core/classes/Enumerable.html
Warto też wziąć pod uwagę kontekst. Poza each masz wiele innych metod, które akceptują blok i “coś tam robią z tablicą”. Taki kod jest po prostu bardziej spójny:
[code=ruby][1,2,3,4].select{|e| e % 2 ==0}.each{|e| puts e}
versus
tab = [1,2,3,4]
tab1 = []
for i in 1…tab.size
tab1 << tab[i] if tab[i] % 2 == 0
end
for i in 1…tab1.size
puts tab1[i]
end[/code]
Można powiedzieć, że to tylko pragmatyzm, ale ten pragmatyzm sprawia, że programowanie w Rubim dla wielu ludzi jest frajdą.
No tak… Ale w sumie, każda z tych metod jest w środku i tak poprzez taką pętlę for (lub inną, nie wiem bo nie patrzałem w kod źródłowy Rubiego) zaimplementowana. Zatem to tylko taka ładna obudowa. I właśnie tego chciałem się dowiedzieć
Nie do końca. Tak jak wspomniałem, nie wszystkie iteratory mają dostęp poprzez indeks, np. wczytywanie linia po linii z pliku jest sekwencyjne i nie można odwołać się do konkretnej linii (chyba, że wczytamy wszystkie linie do tablicy). Zatem implementacja takiego iteratora zapewne wygląda tak:
# pseudo kod
class File
def each_line
while line = self.readline
yield line
end
end
end