Oto pierwszy warsztat 🔧 migawki. Celem warsztatu jest poprawienie prostego skryptu i pokazanie jak rozwiązujemy codzienne problemy oraz na co zwracamy uwagę przy ocenie jakości kodu. “Pierwsza wersja wszystkiego to śmieci”, my, też przejdziemy przez parę iteracji, ale mam nadzieje, że efekt końcowy zaskoczy Cię tak samo jak mnie😱
Jest to eksperyment na łamach migawki, więc bardzo liczę na waszą opinie. Daj znać w komentarzach czy wpis się wam podobał. Jeśli stwierdzicie, że nie, to skupimy się na tradycyjnych artykułach😃 Natomiast przy pozytywnym odbiorze – w kolejce czeka kolejny algorytm do warsztatu migawki.
Sekretny Mikołaj
Sekretny Mikołaj to zabawa, gdzie grupa osób umawia się na zrobienie sobie wzajemnie małych niespodzianek. Do kapelusza wkładamy imiona wszystkich uczestników zapisane na karteczkach. Następnie każdy losuje karteczkę. Jeśli wylosował swoje imię, to losuje jeszcze raz. Jeśli wylosował kogoś innego, to szykujemy mały prezent dla tej osoby. Ważne, aby nie zdradzać wylosowanych imion!
Oryginalny kod
Oryginalny kod, który wziąłem na warsztat, znalazłem na jednej z grup Pythonowych. Autor, tego kodu, nazwijmy go Mikołaj, zaczyna swoją przygodę z Pythonem i przygotował taki prosty skrypt:
Skrypt jest całkiem prosty, jednak zawiera pewien drobny błąd:
O ile raz na jakiś czas się trafi takie dopasowanie, o tyle gdy nie znajduje to wyrzuca błąd.
Rzeczy do poprawy
Mamy tutaj kilka problemów, które będziemy rozwiązywać:
- Skrypt ma działać zawsze
- Nazewnictwo zmiennych (osoby i osoby2 nie mówią nic o przeznaczeniu tych zmiennych, tylko o zawartości)
- Nadpisywanie zmiennych (w pętli for używamy tych samych przy zwracaniu pojedynczych elementów)
- Powinniśmy podzielić kod w funkcje
- Brakuje
if __name__ == “__main__”
Pierwsza iteracja
Widząc te proste problemy, rzuciłem się w wir implementacji. Przecież taki kod to chleb powszedni dla takiego wymiatacza jak ja! Na początek postanowiłem rozwiązać pierwsze trzy problemy, były one najważniejsze.
Brak wyjątków
Przede wszystkim skrypt ma działać, to najważniejszy cel. Problem postanowiłem rozwiązać implementując kilka prostych kroków:
- iterowanie po wszystkich uczestnikach zabawy
- usunięcie aktualnie losującej osoby z puli losowanych
- losowanie osoby, której podarujemy prezent
Nazewnictwo
Drugi problem, czyli nazewnictwo, jest bardzo często ignorowany w świecie IT. Jednak oceńcie sami. Lepiej nazwać zmienne osoby
i osoby2
, czy może givers
(dający) i recipients
(odbiorcy)? Przy takich nazwach wiemy, że listy zawierają osoby, które mają nam coś dać i osoby, które mają coś otrzymać. Dodaliśmy kontekst!
Nadpisywanie zmiennych
Trzeci problem, czyli nadpisywanie zmiennych. Tego zwyczajnie nie powinniśmy robić. Chociaż nasz skrypt będzie działać, to wprowadza to zamieszanie i może powodować powstawanie w przyszłości nowych nieoczekiwanych błędów.
Rozwiązanie
Mając na uwadze powyższe problemy, przygotowałem na szybko ich rozwiązanie. Co mnie bardzo cieszy, udało się pozbyć if-ów z kodu. Ifologia zawsze zmniejsza czytelność i jest przyczyną powstawania kodu legacy. Taki prosty nawyk, stosowany systematycznie nawet w najprostszych skryptach gwarantuje sukces w dużych projektach.
Uruchomiłem skrypt kilka razy. Wszystko działało, więc rozwiązanie zostało opublikowane na grupie. Pełen sukces! 🎉
Houston, mamy problem
Życie okazało się jednak być okrutne. W kodzie był błąd!
Jeśli pod koniec działania skryptu w tabeli recipients
zostanie tylko osoba, która jeszcze nie losowała karteczki, powstaje ten błąd.
Fala pomysłów
Oczywiście internet zaszumiał falą pomysłów na ominięcie błędu: “Dodajmy ify”, “Dodajmy try/except”, “Ponów wykonanie funkcji, aż się uda”. Ja im wszystkim mówię stanowcze NIE! Dlaczego? Pomysły wprawdzie sprawiają, że skrypt zaczyna działać, jednak nie rozwiązują problemu. Zostaje on tylko ukryty, a to może się na nas zemścić. Praktyka pokazała mi, że to zawsze się dzieje. Prędzej czy później, ale własnoręcznie popełniony zły kod i tak nas dopadnie!
Nastał piątek
O błędzie dowiedziałem się w piątek. Wiedziałem, że musi być lepsze rozwiązanie, że możemy uniknąć ifologi i rozwiązać ten problem sprytem! Nie miałem jednak jak tego zrobić przez trzy długie, trzy bardzo długie dni. Cały weekend nie miałem dostępu do komputera 😭
Druga iteracja
Nastał koniec weekendu, wreszcie można wrócić do algorytmu. Tym razem podchodzę do niego jak na seniora przystało.
Wymyślanie koła na nowo
Przeszukując internet trafiłem na ten same problemy. Większość materiałów proponowała liczne iteracje, skomplikowaną ifologię. Kilka artykułów skupiało się nawet na rozwiązaniach opartych o grafy. To wszystko wydawało się zbyt skomplikowane lub nieestetyczne. Jednak w jednym z artykułów o grafach trafiłem na złotego grala!
Rozwiązanie bez if-ów i pętli
Algorytm polega na trzech bardzo prostych operacjach:
- wymieszaj listę osób
- stwórz kopię osób i przesuń ją o jeden
- przypisz do siebie osoby o odpowiednich indeksach z obu list
Oto ostateczna wersja?
Rzeczy do poprawy – sprawdzenie
Jeśli sprawdzimy naszą początkową listę z zaobserwowanymi problemami zobaczysz, że wszystkie zostały już rozwiązane. Program przede wszystkim działa. Dla 1 miliona uruchomień otrzymałem 0 błędów! Nazwy są przejrzyste, kod został podzielony na funkcje i dodałem brakujący warunek if __name__ == “__main__”
.
Jednak co najważniejsze, uniknęliśmy zbędnych pętli, if-ów, sprawdzania warunków brzegowych, ponawiania algorytmu w przypadku problemów. Uzyskaliśmy, czysty i przejrzysty kod.
Dodatkowy zysk
Implementacja zawiera jeszcze jedną ciekawą zaletę. Funkcja reprezentująca główną logikę, czyli match_persons
, nie definiuje sposobu prezentacji wyniku. Jest on prezentowany dopiero w funkcji secret_santa
. Pozwoli nam to w prosty sposób przetestować działanie tej funkcji.
Niech prowadzą mnie testy
Czy można było zrobić wszystko lepiej i szybciej? Oczywiście, że tak. Gdybym do tego problemu podszedł stosując TDD prawdopodobnie uniknąłbym publikowania rozwiązania, które zawierało błąd. Jeśli chcecie uniknąć takich sytuacji to zapraszam was do serii Piotrka o TDD.
Podsumowanie
Celem tego warsztatu było pokazanie kilku rzeczy, które warto zapamiętać na przyszłość:
- nawet proste problemy można rozwiązać w ciekawy sposób
- nie każdy błąd należy poprawiać dodatkowym if-em
- w internecie jest ogrom złych rozwiązań
Spodobała ci się taka forma postów? Chcesz więcej? Jeśli tak, to my również😃 Nie dowiemy się jednak o tym, bez feedbacku z twojej strony. Jak możesz pomóc? Zostaw komentarz lub łapkę pod tym artykułem. Dzięki.