Ruby/Iteratory: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
→‎Iteratory: -> złe kolorowanie składni
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:
 
<sourcesyntaxhighlight lang="cpp">
char *str;
for (str = "abcdefg"; *str != '\0'; str++) {
/* tutaj przetwarzamy znak */
}
</syntaxhighlight>
</source>
 
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. Między innymi dlatego, C jest odbierany 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:
 
<sourcesyntaxhighlight lang="bash">
#!/bin/sh
 
Linia 22:
# ... tutaj byłby kod do wykonania dla każdego pliku
done
</syntaxhighlight>
</source>
 
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:
 
<sourcesyntaxhighlight lang="ruby">
ruby> "abc".each_byte { |c| printf "<%c>", c }
#=> <a><b><c>
</syntaxhighlight>
</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...
 
<sourcesyntaxhighlight lang="c">
s = "abc"
i = 0
Linia 47:
end
#=> <a><b><c>
</syntaxhighlight>
</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.
Linia 53:
Innym iteratorem klasy <tt>String</tt> jest <tt>each_line</tt>.
 
<sourcesyntaxhighlight lang="ruby">
"a\nb\nc\n".each_line { |l| print l }
#=> a
# b
# c
</syntaxhighlight>
</source>
 
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 64:
Instrukcja <tt>for</tt> pojawiająca się w [[Ruby/Struktury sterujące|rozdziale o instrukcjach sterujących]] 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>:
 
<sourcesyntaxhighlight lang="ruby">
for l in "a\nb\nc\n"
print l
Linia 71:
# b
# c
</syntaxhighlight>
</source>
 
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.
 
<sourcesyntaxhighlight lang="ruby">
c = 0
for i in 0..4
Linia 87:
#=> 012
# 01234
</syntaxhighlight>
</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:
Linia 98:
<tt>yield</tt> jak już wiemy, jest wyrażeniem, które przenosi sterowanie do bloku kodu który został przekazany do iteratora. Używając instrukcji <tt>yield</tt> i <tt>retry</tt> można zdefiniować iterator który będzie działał mniej więcej jak standardowa pętla <tt>while</tt>.
 
<sourcesyntaxhighlight lang="ruby">
def WHILE(warunek)
return if not warunek
Linia 107:
i=0
WHILE(i < 3) { print i; i+=1 } #=> 012
</syntaxhighlight>
</source>
 
Jak więc widzimy, iteratory w Rubim są metodami obsługującymi przekazane do nich domknięcia. Owszem, istnieją pewne ograniczenia, ale możesz pisać własne oryginalne iteratory. Szczególnie, gdy definiujemy nowy typ danych, wygodnie jest zdefiniować odpowiednie iteratory które będą na nim operować. W tym kontekście powyższe przykłady nie są szczególnie użyteczne. Poznajmy zatem bardziej praktyczne iteratory.
Linia 117:
Przekazuje do bloku każdy element kolekcji. Zwraca <tt>true</tt>, jeśli blok nigdy nie zwróci <tt>false</tt> (lub <tt>nil</tt>).
 
<sourcesyntaxhighlight lang="ruby">
[1, 2, 5].all? { |element| element <= 5 } #=> true
[1, 2, 5].all? { |element| element <= 4 } #=> false
Linia 126:
element < 5
end #=> false
</syntaxhighlight>
</source>
 
Wyjście:
Linia 140:
Zwraca <tt>true</tt>, jeśli przekazany do bloku element kiedykolwiek zwróci <tt>true</tt>.
 
<sourcesyntaxhighlight lang="ruby">
[1, 2, 5].any? { |element| element > 5 } # => false
[1, 2, 5].any? { |element| element == 2} # => true
</syntaxhighlight>
</source>
 
==== <tt>collect(map)</tt> ====
Linia 149:
Przekazuje do bloku każdy element kolekcji, następnie tworzy nową - z elementów zwracanych przez blok.
 
<sourcesyntaxhighlight lang="ruby">
%w{kot tulipan parowka}.collect { |element| element.upcase }
# => ["KOT", "TULIPAN", "PAROWKA"]
[1, 2, 3].collect { |element| element + 1}
#=> [2, 3, 4]
</syntaxhighlight>
</source>
 
==== <tt>collect!(map!)</tt> ====
Linia 160:
Działa jak <tt>collect</tt>, z tą jednak różnicą, że operacji kolekcja dokonuje na sobie, w każdej iteracji zmieniając swoją zawartość.
 
<sourcesyntaxhighlight lang="ruby">
a = [1, 2, 3] #=> [1, 2, 3]
a.collect! { |element| element + 1 } #=> [2, 3, 4]
a #=> [2, 3, 4]
</syntaxhighlight>
</source>
 
==== <tt>delete_if</tt> ====
Linia 170:
Usuwa z kolekcji elementy, dla których blok zwraca <tt>true</tt>
 
<sourcesyntaxhighlight lang="ruby">
[1, 2, 3, 4, 5, 6].delete_if { |i| i%2 == 0 } # => [1, 3, 5]
</syntaxhighlight>
</source>
 
==== <tt>detect(find)</tt> ====
Linia 178:
Zwraca pierwszy element, dla którego blok zwróci <tt>true</tt>
 
<sourcesyntaxhighlight lang="ruby">
(36..100).detect { |i| i%7 == 0 } # => 42
</syntaxhighlight>
</source>
 
==== <tt>downto</tt> ====
Linia 186:
Wykonuje blok, podając w kolejności malejącej liczby od siebie samej do podanej jako parametr.
 
<sourcesyntaxhighlight lang="ruby">
9.downto(0) { |i| print i } #=> 9876543210
</syntaxhighlight>
</source>
 
==== <tt>each</tt> ====
Linia 194:
Przekazuje do bloku każdy z elementów kolekcji
 
<sourcesyntaxhighlight lang="ruby">
['pies', 'kot', 'ryba'].each { |word| print word + " " }
(0..9).each { |i| print i }
#=> pies kot ryba 0123456789
</syntaxhighlight>
</source>
 
==== <tt>each_index</tt> ====
Linia 204:
Działa jak each, ale przekazuje sam indeks każdego elementu.
 
<sourcesyntaxhighlight lang="ruby">
[3, 6, -5].each_index { |i| print i.to_s + " " }
#=> 0 1 2
</syntaxhighlight>
</source>
 
==== <tt>each_with_index</tt> ====
Linia 213:
Przekazuje jednocześnie element i jego indeks do bloku.
 
<sourcesyntaxhighlight lang="ruby">
["jeden", 2, "trzy"].each_with_index do |element, index|
puts "Indeksowi #{index} przyporzadkowalem #{element}"
Linia 221:
# Indeksowi 1 przyporzadkowalem 2
# Indeksowi 2 przyporzadkowalem trzy
</syntaxhighlight>
</source>
 
==== <tt>find_all</tt> ====
Linia 227:
Zwraca wszystkie elementy kolekcji, dla których blok zwróci <tt>true</tt>.
 
<sourcesyntaxhighlight lang="ruby">
(0..30).find_all { |i| i%9 == 0 } #=> [0, 9, 18, 27]
</syntaxhighlight>
</source>
 
==== <tt>grep</tt> ====
Linia 235:
Zwraca elementy spełniające dopasowanie podane jako parametr. Jeśli podano blok, przekazuje do niego tylko te elementy i zwraca tablicę zbudowaną z wartości zwracanych przez blok.
 
<sourcesyntaxhighlight lang="ruby">
# Zwraca wyrazy zawierajace litere 'r'
%w{ruby python perl php}.grep(/r/) do |w|
Linia 243:
 
#=> RUBY PERL
</syntaxhighlight>
</source>
 
==== <tt>inject</tt> ====
Linia 249:
Przekazuje do bloku każdy element kolekcji. Posiada dodatkowo pamięć, która początkowo jest równa pierwszemu elementowi (lub wartości podanej jako parametr). Po zakończeniu każdej iteracji pamięć jest aktualizowana do wartości zwracanej przez blok.
 
<sourcesyntaxhighlight lang="ruby">
# Zwraca największą liczbę z tablicy
a = [-5, 2, 10, 17, -50]
Linia 260:
mem *= element
end #=> 120
</syntaxhighlight>
</source>
 
==== <tt>partition</tt> ====
Linia 266:
Zwraca dwie tablice: jedną z elementami, dla których blok zwraca <tt>true</tt> i drugą - z resztą.
 
<sourcesyntaxhighlight lang="ruby">
(1..6).partition { |i| i%2 == 0 } #=> [[2, 4, 6], [1, 3, 5]]
</syntaxhighlight>
</source>
 
==== <tt>reject</tt> ====
Linia 274:
Odrzuca z kolekcji wszystkie elementy, dla których blok zwróci <tt>true</tt>.
 
<sourcesyntaxhighlight lang="ruby">
(1..10).reject { |i| i >= 3 and i <= 7 } #=> [1, 2, 8, 9, 10]
</syntaxhighlight>
</source>
 
==== <tt>reject!</tt> ====
Linia 282:
Wyrzuca z siebie elementy, dla których blok zwraca <tt>true</tt>.
 
<sourcesyntaxhighlight lang="ruby">
a = (1..10).to_a # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a.reject! { |i| i >= 3 and i <= 7 } # => [1, 2, 8, 9, 10]
a # => [1, 2, 8, 9, 10]
</syntaxhighlight>
</source>
 
==== <tt>reverse_each</tt> ====
Linia 292:
Działa jak <tt>each</tt> tyle, że podaje elementy w odwrotnej kolejności.
 
<sourcesyntaxhighlight lang="ruby">
(0..9).to_a.reverse_each { |i| print i }
#=> 9876543210
</syntaxhighlight>
</source>
 
==== <tt>step</tt> ====
Linia 301:
Przekazuje do bloku wartości od, do - z określonym krokiem.
 
<sourcesyntaxhighlight lang="ruby">
# (1)
0.step(100, 10) { |i| puts i}
# (2)
(0..100).step(10) { |i| puts i }
</syntaxhighlight>
</source>
 
W obu przypadkach wyjście będzie wyglądało tak:
Linia 328:
Wykonuje dany blok określoną ilość razy.
 
<sourcesyntaxhighlight lang="ruby">
5.times { puts "Hej!" }
5.times { |i| print "#{i} "}
Linia 338:
# Hej!
# 0 1 2 3 4
</syntaxhighlight>
</source>
 
==== <tt>upto</tt> ====
Linia 344:
Iteruje blok, przekazując liczby od, do.
 
<sourcesyntaxhighlight lang="ruby">
1.upto(3) { |i| print i }
#=> 123
</syntaxhighlight>
</source>
<noinclude>
{{Nawigacja|Ruby|