Zanurkuj w Pythonie/plural.py, etap 2

Dodamy teraz warstwę abstrakcji. Zaczęliśmy od zdefiniowania listy reguł: jeśli jest tak, wtedy zrób tak, w przeciwnym przypadku idź do następnej reguły. Teraz skomplikujemy pewną część programu po to, by móc uprościć inną.

Przykład 17.6. plural2.py

import re

def match_sxz(noun):                          
    return re.search('[sxz]$', noun)          

def apply_sxz(noun):                          
    return re.sub('$', 'es', noun)            

def match_h(noun):                            
    return re.search('[^aeioudgkprt]h$', noun)

def apply_h(noun):                            
    return re.sub('$', 'es', noun)            

def match_y(noun):                            
    return re.search('[^aeiou]y$', noun)      
        
def apply_y(noun):                            
    return re.sub('y$', 'ies', noun)          

def match_default(noun):                      
    return 1                                  
        
def apply_default(noun):                      
    return noun + 's'                         

rules = ((match_sxz, apply_sxz),
         (match_h, apply_h),
         (match_y, apply_y),
         (match_default, apply_default)
         )                                     #(1)

def plural(noun):                             
    for matchesRule, applyRule in rules:       #(2)
        if matchesRule(noun):                  #(3)
            return applyRule(noun)             #(4)
  1. Choć ta wersja jest bardziej skomplikowana (z pewnością jest dłuższa), robi ona dokładnie to samo: próbuje dopasować kolejno cztery reguły, a następnie, jeśli dopasowanie się powiedzie, stosuje ona odpowiednie wyrażenie regularne. Różnica polega na tym, że każda reguła dopasowująca oraz modyfikująca jest zdefiniowana w swojej własnej funkcji, przy czym funkcje te zostały zebrane w zmiennej rules, która jest krotką krotek.
  2. Używając pętli for, możemy z krotki rules wyciągać po dwie reguły na raz (jedną dopasowującą i jedną modyfikującą). Podczas pierwszej iteracji pętli for matchesRule przyjmie wartość match_sxz, a applyRule wartość apply_sxz. Podczas drugiej iteracji (jeśli taka nastąpi), matchesRule przyjmie wartość match_h, a applyRule przyjmie wartość apply_h.
  3. Pamiętajcie, że w jezyku Python wszystko jest obiektem, nawet funkcje. Krotka rules składa się z dwuelementowych krotek zawierających funkcje. Nie są to nazwy funkcji, lecz rzeczywiście funkcje. W pętli są one przypisywane do applyRule oraz matchesRule, które stają się funkcjami, a więc obiektami, które można wywołać. W tym miejscu podczas w pierwszej iteracji pętli zostanie wykonany kod równoważny wywołaniu: match_sxz(noun).
  4. W tym zaś miejscu podczas pierwszej iteracji pętli for zostanie wykonany kod równoważmy wywołaniu apply_sxz(noun).

Jeśli ten dodatkowy poziom abstrakcji wydaje się zagmatwany, spróbujmy "odwikłać" powyższą funkcję w celu lepszego uwidocznienia równoważności. Pętla w funkcji plural jest równoważna następującej pętli:

Przykład 17.7. Rozwikływanie funkcji plural

def plural(noun):
    if match_sxz(noun):
        return apply_sxz(noun)
    if match_h(noun):
        return apply_h(noun)
    if match_y(noun):
        return apply_y(noun)
    if match_default(noun):
        return apply_default(noun)

Zysk jest taki, że funkcja plural znacznie się uprościła. Bierze ona listę reguł zdefiniowanych w innym miejscu i w sposób bardzo ogólny iteruje po nich: bierze regułę dopasowującą; czy reguła pasuje? Jeśli tak, wywołuje regułę modyfikującą. Reguły mogą być zdefiniowane w innym miejscu, w dowolny sposób. Funkcji plural pochodzenie reguł nie interesuje.

Zastanówmy się, czy warto było wprowadzać tę warstwę abstrakcji. Raczej nie. Zastanówmy się, co musielibyśmy zrobić, aby dodać do funkcji nową regułę. Cóż, w poprzednim podrozdziale e do funkcji plural należałoby dodać instrukcję if. W tym podrozdziale należałoby dodać dwie funkcje, macth_foo i apply_foo, a następnie zaktualizować listę reguł wstawiając je w takim miejscu, żeby w stosunku do innych reguł zostały one wywołane w odpowiedniej kolejności.

Tak naprawdę to było tylko wprowadzenie do kolejnego podrozdziału. Idźmy więc dalej.