Zanurkuj w Pythonie/plural.py, etap 1
Patrzymy na słowa, które - przynajmniej w języku angielskim - składają się z liter. Dysponujemy też regułami, które mówią, że musimy znaleźć w słowie pewne kombinacje liter, a następnie odpowiednio to słowo zmodyfikować. Brzmi to dokładnie jak zadanie dla wyrażeń regularnych.
Przykład 17.1. plural1.py
import re
def plural(noun):
if re.search('[sxz]$', noun): #(1)
return re.sub('$', 'es', noun) #(2)
elif re.search('[^aeioudgkprt]h$', noun):
return re.sub('$', 'es', noun)
elif re.search('[^aeiou]y$', noun):
return re.sub('y$', 'ies', noun)
else:
return noun + 's'
- Rzeczywiście, jest to wyrażenie regularne, jednak używa ono składni, jakiej w rozdziale 7 (Wyrażenia regularne) nie widzieliście. Nawias kwadratowy oznacza: "dopasuj dokładnie jeden z wymienionych tu znaków". A więc [sxz] oznacza "s albo x, albo z", ale tylko jeden znak na raz. Znak $ powinien być wam znany; dopasowuje się on do końca napisu. A więc sprawdzamy tutaj, czy rzeczownik kończy się na jedną z liter s, x lub z.
- Funkcja re.sub dokonuje podstawienia w oparciu o wyrażenie regularne. Przyjrzyjmy się jej bliżej.
Przykład 17.2. Wprowadzenie funkcji re.sub
>>> import re >>> re.search('[abc]', 'Mark') #(1) <_sre.SRE_Match object at 0x001C1FA8> >>> re.sub('[abc]', 'o', 'Mark') #(2) 'Mork' >>> re.sub('[abc]', 'o', 'rock') #(3) 'rook' >>> re.sub('[abc]', 'o', 'caps') #(4) 'oops'
- Czy napis Mark zawiera jedną z liter a, b lub c? Tak, zawiera a.
- W porządku; znajdź a, b lub c i zastąp je literą o. Mark zmienia się w Mork.
- Ta sama funkcja zmienia rock w rook.
- Może się wydawać, że ta linia zmieni caps w oaps, jednak dzieje się inaczej. Funkcja re.sub zastępuje wszystkie wystąpienia, nie tylko pierwsze. Caps zmienia się w oops ponieważ zarówno c jak i a zostają zastąpione literą o.
Przykład 17.3. Z powrotem do plural1.py
import re
def plural(noun):
if re.search('[sxz]$', noun): #(1)
return re.sub('$', 'es', noun)
elif re.search('[^aeioudgkprt]h$', noun): #(2)
return re.sub('$', 'es', noun)
elif re.search('[^aeiou]y$', noun): #(3)
return re.sub('y$', 'ies', noun)
else:
return noun + 's'
- Wróćmy do funkcji plural. Co robimy? Zamieniamy końcówkę napisu na "es". Innymi słowy, dodajemy "es" do napisu. Moglibyśmy osiągnąć ten cel używając dodawania napisów, na przykład stosując wyrażenie: rzeczownik + "es", jednak tutaj wyrażeń regularnych będę używał do wszystkiego, ze względu na spójność oraz z innych powodów, które zostaną wyjaśnione w dalszej części rozdziału.
- Patrzcie uważnie, to kolejna nowość. Znak ^ znajdujący się wewnątrz nawiasów kwadratowych oznacza coś szczególnego: negację. [^abc] oznacza "dowolny znak oprócz a, b oraz c". Wyrażenie [^aeioudgkprt] oznacza "każdy znak za wyjątkiem a, e, i, o, u, d, g, k, p, r oraz t. Po tym znaku powinien znaleźć się znak h kończący napis. Tutaj szukamy słów kończących się na H, które można usłyszeć.
- Tutaj podobnie: dopasowujemy słowa kończące się na Y, przy czym znak stojący przed Y musi być dowolnym znakiem za wyjątkiem a, e, i, o oraz u. Szukamy słów, które kończą się na Y i brzmią jak I.
Przykład 17.4. Więcej na temat negacji w wyrażeniach regularnych
>>> import re >>> re.search('[^aeiou]y$', 'vacancy') #(1) <_sre.SRE_Match object at 0x001C1FA8> >>> re.search('[^aeiou]y$', 'boy') #(2) >>> >>> re.search('[^aeiou]y$', 'day') >>> >>> re.search('[^aeiou]y$', 'pita') #(3) >>>
- Wyrażenie zostanie dopasowane do "vacancy" ponieważ słowo to kończy się na cy, a c nie jest a, e, i, o ani u.
- Nie zostanie dopasowane do "boy", który kończy się na oy, a powiedzieliśmy wyraźnie, że znakiem stojącym przed y nie może być o. "day" nie zostanie dopasowane, ponieważ kończy się na ay.
- "pita" również nie zostanie dopasowana, ponieważ nie kończy się na y.
Przykład 17.5. Więcej na temat re.sub
>>> re.sub('y$', 'ies', 'vacancy') #(1) 'vacancies' >>> re.sub('y$', 'ies', 'agency') 'agencies' >>> re.sub('([^aeiou])y$', r'\1ies', 'vacancy') #(2) 'vacancies'
- To wyrażenie regularne przekształca "vacancy" w "vacancies" oraz "agency" w "agencies", dokładnie tak, jak chcemy. Zauważmy, że wyrażenie to przekształciłoby "boy" w "boies", gdyby nie fakt, że w funkcji użyliśmy wcześniej re.search, aby dowiedzieć się, czy powinniśmy również dokonać podstawienia przy użyciu re.sub.
- Chciałbym nadmienić mimochodem, że możliwe jest połączenie tych dwóch wyrażeń regularnych (jednego, które sprawdza, czy pewna zasada ma zastosowanie, i drugiego, które faktycznie tę zasadę stosuje) w jedno. Wyglądałoby ono dokładnie tak. Większość powinna być już wam znana: aby zapamiętać znak, który stoi przed y, używamy zapamiętanej grupy, o której była mowa w podrozdziale 7.6 (Analiza przypadku: Przetwarzanie numerów telefonów). W podstawianym napisie pojawia się nowa składnia, \1, które oznacza: "czy pamiętasz grupę numer 1? wstaw ją tutaj". W tym przypadku jako znak stojący przed y zostanie zapamiętane c, po czym dokonane zostanie podstawienie c w miejsce c oraz ies w miejsce y. (Jeśli potrzebujemy więcej niż jednej zapamiętanej grupy, używamy \2, \3 itd.)
Podstawienia wyrażeń regularnych stanowią niezwykle silny mechanizm, a składnia \1 czyni go jeszcze silniejszym. Z drugiej strony przedstawienie całej operacji w postaci jednego wyrażenia regularnego sprawiłoby, że stałaby się ona mało czytelna i niewiele by miała wspólnego ze sposobem, w jaki na początku opisywaliśmy sposób konstruowania liczby mnogiej. Utworzyliśmy reguły takie jak "jeśli słowo kończy się na S, X lub Z, dodaj ES" i kiedy teraz patrzymy na funkcję plural, widzimy dwie linijki kodu, które mówią "jeśli słowo kończy się na S, X lub Z, dodaj ES". Nie można tego zrobić bardziej bezpośrednio.