Napisz funkcję sprawdzającą, czy dane jako parametr słowo to palindrom.

Zatem funkcja powinna wyglądać następująco:

def is_palnidrom(word):
    """Funkcja sprawdza czy podany wyraz jest palindromem

    Arguments:
        word {str} -- sprawdzane słowo

    Returns:
        bool -- True jeśli słowo jest palindromem. W innym wpadku False
    """
    pass

Co to ten palindrom?

No to szybkie przypomnienie. Jeśli dany wyraz jest palindromem oznacza to, że czytając go zarówno od prawej do lewej, jak i od lewej do prawej wyraz będzie brzmieć tak samo.

Wiki mówi coś takiego

Palindrom (gr. palindromeo – biec z powrotem) – wyrażenie brzmiące tak samo czytane od lewej do prawej i od prawej do lewej. Przykładem palindromu jest: Kobyła ma mały bok. Współcześnie palindromy pełnią funkcję gry słownej. Prawdopodobnie tak było również i w przeszłości, choć pewne znaleziska sugerują, że palindromy mogły też mieć znaczenie magiczne.”

By nie komplikować, nasza funkcja będzie sprawdzać poprawność pojedynczego słowa. Doskonałym przykładem (i jedynym jaki znam) jest słowo ”kajak”.

Podejście pierwsze

def is_palnidrom(word):
    palnidrom = True
    for x in range(len(word)):
        if word[x] != word[len(word)-1-x]:
            return False

    return palnidrom

print(is_palnidrom('kajak'))
print(is_palnidrom('kajjak'))
print(is_palnidrom('kajak'))
print(is_palnidrom('kajjakk'))
True
True
True
False

No umówmy się, ten kod nie jest najpiękniejszy. Dodatkowo moim zdaniem dość skomplikowany jak na funkcjonalność, którą oferuje. Pamiętajmy, że Python stawia na czytelność kodu. Wykorzystaliśmy tu funkcjęrange” do wygenerowania indeksów, które potem wykorzystujemy do iteracji po naszym wyrazie.

A może lista składana?

Wiadomo dzień bez listy składanej dniem straconym.

def is_palnidrom(word):
    return all([x == y for x, y in zip(word, word[::-1])])

print(is_palnidrom('kajak'))
print(is_palnidrom('kajjak'))
print(is_palnidrom('kajak'))
print(is_palnidrom('kajjakk'))

Output:

True
True
True
False

Chcieliśmy listę składaną no i mamy. Nawet działa. Problem w tym, że użyliśmy jej tu jakby na siłę. Oczywiście użyliśmy funkcjizip” aby przeliterować się po obydwu listach. Zabłysnęliśmy również znajomością wbudowanej funkcjiall” by sprawdzić czy korespondencyjne litery są takie same. Zwróćcie również uwagę na pythonowy trik na sworznie odwrotnej listyword[::-1]”.

Mały krok dla człowieka …

def is_palnidrom(word):
    return all(x == y for x, y in zip(word, word[::-1]))

print(is_palnidrom('kajak'))True
print(is_palnidrom('kajjak'))
print(is_palnidrom('kajak'))
print(is_palnidrom('kajjakk'))

Output:

True
True
True
False

Widzisz co tu się zmieniło? Zgubiliśmy dwa takie znaczki ”[” i ”]” z naszej listy składanej. Oznacza to, że nasza lista składana przestała być listą składaną. Od teraz awansowała na posadę generatora. Generatory znacząco różnią się od list. Mówi się, że są leniwe. Nie nie jest to obelga, a raczej główna zaleta generatorów. Ich leniwość objawia się podczas zwracaniu nam kolejnych danych. W przeciwieństwie do list nie serwują nam całych danych za jednym zamachem. Generatory zwracają nam dane jedna po drugiej w momencie kiedy sami się o nie upomnimy. Znacznie oszczędza to pamięć. Dodatkowo funkcja all” zwróci ”False” zaraz po wykryciu pierwszej negatywnej wartości. Czyli po stwierdzeniu pierwszej niezgodnej pary liter.

Jak nie robić roboty dwa razy

Udało nam się napisać całkiem fajny generator. Jednak jest z nim mały problem. Mało tego ten problem występował we wszystkich naszych implementacjach. Otóż sprawdzamy wszytko dwa razy.

Z uwagi na to, że palindromy są niejako symetryczne wystarczyło by sprawdzić tylko pół wyrazu z jego drugim odpowiednikiem. Do tej pory sprawdzaliśmy całe słowo co skutkowałem sprawdzaniem tych samych liter dwa razy. Oto jak tego nie robić:

def is_palnidrom(word):
    return all(
            x == y 
            for x, y in zip(word[:int(len(word)/2)], word[::-1])
    )

Output:

True
True
True
False

Pewnie już zauważyłeś, że jedna z list po których iterujemy się za pomocą funkcjizip” jest krótsza. To nic nie szkodzi bowiem funkcjazip” przerwie iterowanie gdy krótsza z kolekcji się skończy. Dzięki takiemu prostemu patentowi znacząco zmniejszyliśmy liczbę porównań które musimy wykonać.

Podejście pythonic way

Jak to zwykle bywa w Pythonie najprostsze pomysły są najlepsze. Poniższy przykład jest niezwykle prosty i czytelny. Dodatkowo implementacja porównania dwóch łańcuchów tekstowych za pomocą operatora==” jest dość wydajna. Python porównuje ze sobą znak po znaku z obu kolekcji i natychmiastowo zwraca ”False” po napotkaniu pierwszej niezgodności.

def is_palnidrom(word):
    return word == word[::-1]

print(is_palnidrom('kajak'))
print(is_palnidrom('kajjak'))
print(is_palnidrom('kajak'))
print(is_palnidrom('kajjakk'))

Output:

True
True
True
False

Czy to nie jest piękne. Posłużyliśmy się tu naszym starym trikiem odwracania listy za pomocą slice. Oto przykład użycia:

>>> moja_lista = [1, 2, 3]
>>> moja_lista[::-1]
[3, 2, 1]

Mam nadzieję, że nie jesteś zły że zmusiłem Cię to męczenia się z poprzednimi przykładami jak i tak najprostsze rozwiązanie okazało się najlepsze :). Potraktuj to jak naukę przez zabawę i baw się dobrze. Python to zabawa.