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 funkcji ”zip” aby przeliterować się po obydwu listach. Zabłysnęliśmy również znajomością wbudowanej funkcji ”all” by sprawdzić czy korespondencyjne litery są takie same. Zwróćcie również uwagę na pythonowy trik na sworznie odwrotnej listy ”word[::-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ą funkcji ”zip” jest krótsza. To nic nie szkodzi bowiem funkcja ”zip” 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.