Ruby/Iteratory: Różnice pomiędzy wersjami
Usunięta treść Dodana treść
Szymon wro (dyskusja | edycje) |
Szymon wro (dyskusja | edycje) Nie podano opisu zmian |
||
Linia 7:
Podczas pisania kodu potrzebujemy pętli w wielu różnych sytuacjach. W C, kodujemy je używając <tt>for</tt> lub <tt>while</tt>. Na przykład,
<source lang="cpp">
<pre>▼
char *str;
for (str = "abcdefg"; *str != '\0'; str++) {
/* tutaj przetwarzamy znak */
}
</
Składnia pętli <tt>for (...)</tt> z języka C dostarcza pewnej abstrakcji, która pomaga w utworzeniu pętli, ale sprawdzenie czy <tt>*str</tt> nie wskazuje na znak pusty znak wymaga od programisty znajomości szczegółów o wewnętrznej strukturze łańcucha znakowego. Sprawia to, że C jest odczuwany jako język niskiego poziomu. Języki wyższego poziomu odznaczają się bardziej elastycznym wsparciem iteracji. Rozważ następujący skrypt ''sh'' powłoki systemowej:
<source lang="bash">
<pre>▼
#!/bin/sh
Linia 22:
# ... tutaj byłby kod do wykonania dla każdego pliku
done
</
Wszystkie pliki źródłowe i nagłówkowe języka C w bieżącym katalogu są przetwarzane i powłoka systemowa bierze na siebie detale dotyczące wskazywania i podstawiania po kolei wszystkich nazw plików, jedna po drugiej. To chyba działa na wyższym poziomie niż C, nie sądzisz?
Linia 32:
Typ <tt>String</tt> Rubiego posiada kilka użytecznych iteratorów:
<source lang="ruby">
ruby> "abc".each_byte { |c| printf "<%c>", c }
#=> <a><b><c>
<source>
<tt>each_byte</tt> to iterator wskazujący każdy znak w łańcuchu. Każdy znak jest podstawiany do zmiennej lokalnej <tt>c</tt>. To samo można przełożyć na coś bardziej przypominającego kod C...
<source lang="ruby">
i
i+=1 end
#=> <a><b><c>
</source>
... jednakże iterator <tt>each_byte</tt> jest koncepcyjnie prostszy, i wydaje się, że działałby nadal nawet gdyby klasa <tt>String</tt> uległa w przyszłości radykalnym modyfikacjom. Dużą zaletą iteratorów jest to, że zachowują one swoje poprawne działanie na przekór takim radykalnym zmianom. Jest to charakterystyczna cecha dobrego kodu w ogólności. (Tak, miej cierpliwość, niebawem będziemy mówić również o tym czym są klasy.)
Linia 54 ⟶ 53:
Innym iteratorem klasy <tt>String</tt> jest <tt>each_line</tt>.
<source lang="ruby">
#=>
# a
# b
# c
</
Zadania które wymagałyby dużego wysiłku w C (wyszukiwanie ograniczników linii, generowanie podłańcuchów, itd.) z użyciem iteratorów można wykonać bardzo łatwo.
Linia 66 ⟶ 65:
Instrukcja <tt>for</tt> pojawiająca się w [[Ruby/Struktury sterujące|poprzednim rozdziale]] dokonywała iteracji przez użycie iteratora <tt>each</tt>. Iterator <tt>each</tt> klasy <tt>String</tt> działa w ten sam sposób jak <tt>each_line</tt>, więc przepiszmy powyższy przykład z <tt>for</tt>:
<source lang="ruby">
#=>
# a
# b
# c
</
Możemy używać struktury sterującej <tt>retry</tt> w połączeniu z iterowaną pętlą. Spowoduje ona rozpoczęcie iterowania pętli od początku.
<source lang="ruby">
for i in 0..4
print i
if i
end
▲ | retry
#=>
# 012
# 01234
</source>
Zamienienie <tt>retry</tt> na <tt>redo</tt> w powyższym przykładzie spowoduje, że tylko bieżąca iteracja będzie wykonana ponownie, z następującym wynikiem:
▲<pre>
012
234
▲</pre>
<tt>yield</tt> jest wyrażeniem, które pojawia się czasem w definicji iteratora. <tt>yield</tt> przenosi sterowanie do bloku kodu który został przekazany do iteratora (wyjaśnimy to dogłębniej w [[Ruby/Obiekty procedurowe|rozdziale]] poświęconym obiektom procedurowym). Następujący przykład definiuje iterator <tt>repeat</tt>, który powtarza blok kodu określoną ilość razy przekazaną jako argument.
|