[ Pobierz całość w formacie PDF ]

znaki nowego wiersza. Nale y w tym celu u y innego wyra enia regularnego:
def reformat_wrapped(s, width=78)
s.gsub(/\s+/, " ").gsub(/(.{1,#{width}})( |\Z)/, "\\1\n")
end
54 | Rozdzia 1. a cuchy
Przetwarzanie sterowane wyra eniami regularnymi jest jednak stosunkowo powolne; znacz-
nie efektywniejszym rozwi zaniem byloby podzielenie la cucha na poszczególne slowa i zlo-
enie z nich nowego la cucha, podzielonego na wiersze nieprzekraczaj ce okre lonej dlugo ci:
def reformat_wrapped(s, width=78)
lines = []
line = ""
s.split(/\s+/).each do |word|
if line.size + word.size >= width
lines
line = word
elsif line.empty?
line = word
else
line
end
end
lines
return lines.join "\n"
end
puts reformat_wrapped(prose, 50)
# Czu em si tak samotny tego dnia, jak rzadko
# kiedy, spogl daj c apatycznie na deszcz padaj cy
# za oknem. Jak d ugo jeszcze b dzie pada ? W
# gazecie by a prognoza pogody, ale któ w ogóle
# zadaje sobie trud jej czytania?
Patrz tak e
" W bibliotece Facets Core zdefiniowane s metody String#word_wrap i String#word_
wrap!.
1.16. Generowanie nast pnika a cucha
Problem
Nale y wykona iteracj po ci gu la cuchów zwi kszaj cych si alfabetycznie  w sposób
podobny do iterowania po ci gu kolejnych liczb.
Rozwi zanie
Je li znany jest pocz tkowy i ko cowy la cuch z zakresu obj tego iteracj , mo na do tego za-
kresu (reprezentowanego jako obiekt Range) zastosowa metod Range#each:
('aa'..'ag').each { |x| puts x }
# aa
# ab
# ac
# ad
# ae
# af
# ag
1.16. Generowanie nast pnika a cucha | 55
Metod generuj c nast pnik danego la cucha jest String#succ. Je li nie jest znany la cuch,
na którym nale y sko czy iterowanie, mo na na bazie tej metody zdefiniowa iteracj nie-
sko czon , któr przerwie si w momencie spelnienia okre lonego warunku:
def endless_string_succession(start)
while true
yield start
start = start.succ
end
end
W poni szym przykladzie iteracja jest ko czona w momencie, gdy dwa ostatnie znaki la cu-
cha s identyczne:
endless_string_succession('fol') do |x|
puts x
break if x[-1] == x[-2]
end
# fol
# fom
# fon
# foo
Dyskusja
Wyobra my sobie, e la cuch jest czym na ksztalt (uogólnionego) licznika przejechanych ki-
lometrów  ka dy znak la cucha jest osobn pozycj tego licznika. Na ka dej z pozycji mo-
g pojawia si znaki tylko jednego rodzaju: cyfry, male litery albo wielkie litery3.
Nast pnikiem (successor) la cucha jest la cuch powstaj cy w wyniku zwi kszenia o 1 (inkre-
mentacji) wskazania wspomnianego licznika. Rozpoczynamy od zwi kszenia prawej skrajnej
pozycji; je li spowoduje to jej  przekr cenie na warto pocz tkow , zwi kszamy o 1 s -
siedni pozycj z lewej strony  która te mo e si przekr ci , wi c opisan zasad stosuje-
my rekurencyjnie:
'89999'.succ # => "90000"
'nzzzz'.succ # => "oaaaa"
Je li  przekr ci si skrajna lewa pozycja, dol czamy z lewej strony la cucha now pozycj
tego samego rodzaju co ona i ustawiamy t dodan pozycj na warto pocz tkow :
'Zzz'.succ # => "AAaa"
W powy szym przykladzie skrajna lewa pozycja wy wietla wielkie litery; jej inkrementacja
powoduje  przekr cenie z warto ci Z do warto ci A, dodajemy wi c z lewej strony la cucha
now pozycj , tak e wy wietlaj c wielkie litery, ustawiaj c j na warto pocz tkow A.
Oto przyklady inkrementacji la cuchów zawieraj cych wyl cznie male litery:
'z'.succ # => "aa"
'aa'.succ # => "ab"
'zz'.succ # => "aaa"
W przypadku wielkich liter sprawa ma si podobnie  nale y pami ta , e wielkie i male
litery nigdy nie wyst puj razem na tej samej pozycji:
'AA'.succ # => "AB"
'AZ'.succ # => "BA"
3
Ograniczamy si tylko do liter alfabetu angielskiego a .. z i A .. Z  przyp. t um.
56 | Rozdzia 1. a cuchy
'ZZ'.succ # => "AAA"
'aZ'.succ # => "bA"
'Zz'.succ # => "AAa"
Inkrementowanie cyfr odbywa si w sposób naturalny  inkrementacja cyfry 9 oznacza jej
 przekr cenie na warto 0:
'foo19'.succ # => "foo20"
'foo99'.succ # => "fop00"
'99'.succ # => "100"
'9Z99'.succ # => "10A00"
Znaki niealfanumeryczne  czyli inne ni cyfry, male litery i wielkie litery  s przy inkre-
mentowaniu la cucha ignorowane  wyj tkiem jest jednak sytuacja, gdy la cuch sklada si wy-
l cznie ze znaków tej kategorii. Umo liwia to inkrementowanie la cuchów sformatowanych:
'10-99'.succ # => "11-00"
Je li la cuch sklada si wyl cznie ze znaków niealfanumerycznych, jego pozycje inkremento-
wane s zgodnie z uporz dkowaniem znaków w kodzie ASCII; oczywi cie w wyniku inkre-
mentacji mog pojawi si w la cuchu znaki alfanumeryczne, wówczas kolejna jego inkremen-
tacja odbywa si wedlug regul wcze niej opisanych.
'a-a'.succ # => "a-b"
'z-z'.succ # => "aa-a"
'Hello!'.succ # => "Hellp!"
%q{'zz'}.succ # => "'aaa'"
%q{z'zz'}.succ # => "aa'aa'"
'$$$$'.succ # => "$$$%"
s = '!@-'
13.times { puts s = s.succ }
# !@.
# !@/
# !@0
# !@1
# !@2
# ...
# !@8
# !@9
# !@10
Nie istnieje metoda realizuj ca funkcj odwrotn do metody String#succ. Zarówno twórca
j zyka Ruby, jak i cala wspólnota jego u ytkowników zgodni s co do tego, e wobec ogra-
niczonego zapotrzebowania na tak metod nie warto wklada wysilku w jej tworzenie,
a zwlaszcza poprawn obslug ró nych warunków granicznych. Iterowanie po zakresie la -
cuchów w kierunku malej cym najlepiej jest wykonywa , transformuj c ów zakres na tablic
i organizuj c iteracj po tej e w kierunku malej cych indeksów:
("a".."e").to_a.reverse_each { |x| puts x }
# e
# d
# c
# b
# a
Patrz tak e
" Receptura 2.15,  Generowanie sekwencji liczb .
" Receptura 3.4,  Iterowanie po datach .
1.16. Generowanie nast pnika a cucha | 57
1.17. Dopasowywanie a cuchów
za pomoc wyra e regularnych
Problem
Chcemy sprawdzi , czy dany la cuch zgodny jest z pewnym wzorcem.
Rozwi zanie
Wzorce s zwykle definiowane za pomoc wyra e regularnych. Zgodno ( pasowanie )
la cucha z wyra eniem regularnym testowane jest przez operator =~.
string = 'To jest a cuch 27-znakowy.'
if string =~ /([0-9]+)-character/ and $1.to_i == string.length
"Tak, to jest a cuch #$1-znakowy."
end
# "Tak, to jest a cuch 27-znakowy."
Mo na tak e u y metody Regexp#match:
match = Regexp.compile('([0-9]+)-znakowy').match(string)
if match && match[1].to_i == string.length
"Tak, to jest a cuch #{match[1]}-znakowy."
end
# "Tak, to jest a cuch 27-znakowy."
Za pomoc instrukcji case mo na sprawdzi zgodno la cucha z calym ci giem wyra e
regularnych:
string = "123"
case string
when /^[a-zA-Z]+$/
"Litery"
when /^[0-9]+$/
"Cyfry"
else
"Zawarto mieszana"
end
# => "Cyfry"
Dyskusja
Wyra enia regularne stanowi malo czytelny, lecz u yteczny minij zyk umo liwiaj cy dopaso-
wywanie la cuchów do wzorców oraz ekstrakcj podla cuchów. Wyra enia regularne wykorzy-
stywane s od dawna przez wiele narz dzi uniksowych (jak sed), lecz to Perl byl pierwszym
uniwersalnym j zykiem zapewniaj cym ich obslug . Obecnie wyra enia regularne w stylu
zbli onym do wersji z Perla obecne s w wi kszo ci nowoczesnych j zyków programowania.
W j zyku Ruby wyra enia regularne inicjowa mo na na wiele sposobów. Ka da z poni szych
konstrukcji daje w rezultacie taki sam obiekt klasy Regexp:
/cokolwiek/
Regexp.new("cokolwiek")
Regexp.compile("cokolwiek")
%r{ cokolwiek}
58 | Rozdzia 1. a cuchy
W wyra eniach regularnych mo na u ywa nast puj cych modyfikatorów:
Regexp::IGNORECASE i
Przy dopasowywaniu nieistotna jest wielko liter  ma e litery uto samiane s z ich
wielkimi odpowiednikami.
Regexp:MULTILINE m
Domy lnie dopasowywanie realizowane jest w odniesieniu do a cucha mieszcz cego
si w jednym wierszu. Gdy u yty zostanie ten modyfikator, znaki nowego wiersza
traktowane s na równi z innymi znakami a cucha.
Regexp::EXTENDED x
U ycie tego modyfikatora daje mo liwo bardziej czytelnego zapisu wyra enia
regularnego, przez wype nienie go bia ymi znakami i komentarzami.
Oto przyklad wykorzystania wymienionych powy ej modyfikatorów w definicji wyra enia
regularnego:
/something/mxi
Regexp.new('something',
Regexp::EXTENDED + Regexp::IGNORECASE + Regexp::MULTILINE)
%r{something}mxi [ Pobierz całość w formacie PDF ]