Wyślij zapytanie Dołącz do Sii

Chcąc rozpocząć ścieżkę kariery jako programista Python, zastanawiałem się, jaki sens ma rozwiązywanie zadania „na kartce” podczas rekrutacji.  W tym artykule podzielę się z Wami zadaniem, z którym miałem styczność. Przejdziemy krok po kroku proces jego wytwarzania oraz poznamy kilka właściwości języka.

Zadanie: zamiana ciągu

Naszym zadaniem jest zamiana ciągu `a4b3r6` na `aaaabbbrrrrrr`.

Pierwszą czynnością jest odnalezienie schematu. W tym przypadku podano w treści zadania, że cyfra jest większa od 0, a litera jest z alfabetu ASCII. Wskazano również, że takich par w ciągu wejściowym jest co najmniej 1. Wejście zawsze będzie tego formatu – nie jest wymagana obsługa błędów.

Do rozwiązania zadania użyłem Pythona w wersji 3.x. Wiedza, że Python jest językiem silnie dynamicznie typowanym jest niezbędna do rozpoczęcia pracy. Takie podejście ma swoje wady (wykonywanie operacji na typach, które wspierają tę operację) i zalety (brak potrzeby zarządzania pamięcią). Jest on również językiem interpretowanym – nie ma etapu kompilacji, jest niezależny od architektury, na której jest uruchamiany.

Podejście do rozwiązania zadania

Kiedy już znamy schemat, to wydaje się zasadnym, żeby użyć wyrażenia regularnego (RegEx). Cytując za klasykiem:

Jeżeli coś może zostać wykonane przez wyrażenie regularne, to na pewno robisz to źle.

Z tego powodu to rozwiązanie zostawimy sobie jako wisienkę na torcie, a zaczniemy od podejścia `brute force`.

`brute force`

Zauważmy, że cyfra jest na indeksie (tak, w Pythonie można traktować ciąg znaków jak tablicę) o 1 mniejszym niż litera. Iterując po całym ciągu wejściowym (w Pythonie iteruje się po elementach tablicy – funkcja `range` przekształca liczbę na odpowiednią tablicę; jeśli chcecie poznać cały wykaz możliwości, zachęcam do przeczytania dokumentacji), przy nieparzystej wartości indeksu (tutaj pojawi się cyfra), możemy pobrać literę i iterując ją tyle razy, na ile wskazuje cyfra, konkatenować (w Pythonie konkatenuje się znaki symbolem `+`, a także `+=`, gdzie znak po lewej stronie konkatenujemy ze znakiem po prawej stronie i przypisujemy do zmiennej po lewej stronie.

Wykorzystanie metod

Do łączenia ciągów znaków można również użyć tych metod) w pętli. Przykład, jak to zrobić, możecie sprawdzić w metodzie `stage_one`.

    def stage_one(self, st: str) -> str:  # brute force
        return_string = ''
        for i in range(len(st)):
            if i % 2 == 1:
                for j in range(int(st[i])):
                    return_string += st[i - 1]
        return return_string

Jeżeli lekko zoptymalizujemy poprzedni przykład i podzielimy ciąg wejściowy na paczki składające się z 2 elementów, to pętla główna metody wykona się tylko połowę razy. Użyłem również właściwości języka umożliwiającej „wycięcie” z tabeli zakresu indeksów, co możecie zobaczyć w metodzie `stage_two`.

    def stage_two(self, st: str) -> str:  # lets think about optimisation
        return_string = ''
        for i in range(int(len(st) / 2)):
            st_temp = st[2 * i: 2 * (i + 1)]
            for j in range(int(st_temp[1])):
                return_string += st_temp[0]
        return return_string

Kolejnym etapem usprawnienia będzie pozbycie się właśnie dodanego „wydzielania” przez zauważenie, że cyfra będzie zawsze w tym samym miejscu, dzięki czemu pozbywamy się jednej operacji: metoda `stage_three`.

   def stage_three(self, st: str) -> str:  # more optimisation
        return_string = ''
        for i in range(int(len(st) / 2)):
            for j in range(int(st[2 * i + 1])):
                return_string += st[2 * i]
        return return_string

Metoda ‚stage_four’ wprowadza kolejną właściwość języka Python – mnożenie stringów. Jest właściwym mnożeniem stringów: `6*”alf” = “alfalfalfalfalfalf”`. Ta cecha pozwala nam całkowicie pozbyć się wewnętrznej pętli. 

  def stage_four(self, st: str) -> str:  # let it lok nice too
        return_string = ''
        for i in range(len(st)):
            if i % 2 == 0:
                return_string += str(int(st[i + 1]) * st[i])
        return return_string

Ta metoda byłaby wersją ostateczną – rozwiązaniem zadanego problemu. Po drobnych porządkach mamy metodę `stage_five`.

    def stage_five(self, st: str) -> str:  # final version
        return_string = ''
        for i in range(int(len(st) / 2)):
            return_string += str(int(st[2 * i + 1]) * st[2 * i])
        return return_string

Pojawia się możliwość przedstawienia wersji funkcyjnej tego rozwiązania – metoda `stage_functional`, w której zamiast pętli użyjemy metody `enumerate`, a żeby połączyć ciągi znaków, skorzystamy z metody `join` na pustym znaku.

    def stage_functional(self, str:str) -> str:
        return ''.join(map(lambda x: x[1] * int(str[x[0] + 1]) if x[0] % 2 == 0 else "", enumerate(list(str))))

Wyrażenie regularne

Istnieje drobna obawa o to, czy jesteśmy w stanie coś poradzić, jeżeli zamiast cyfry w ciągu znaków wejściowych pojawi się liczba – zmienią się warunki wejściowe. Tutaj najbardziej ogólnym, wymagającym najmniejszej ilości modyfikacji, rozwiązaniem będzie użycie wyrażeń regularnych – ich odmiana istnieje w języku Python w bibliotece ‚re’, która została zaimportowana i użyta w metodzie `stage_six`.

    def stage_six(self, st: str) -> str:  # regexp bonus :P
        import re
        return_string = ''
        for st_temp in re.findall(r"\D+\d+", st):
            return_string += str(int(st_temp[1]) * st_temp[0])
        return return_string

O wyrażeniach regularnych są pisane książki mające kilkaset stron – każdy język ma swoje podejście do tematu. W dokumentacji biblioteki `re` można przeczytać, że `\d` jest znacznikiem cyfr, a `\D` jest znacznikiem liter.

Podsumujmy, co wiemy

Sprawdźmy, jakie informacje przekazaliśmy rekruterowi na temat naszej wiedzy o języku Python i podejściu do rozwiązania problemu:

  • iteracja po tablicy elementów – funkcja `range`,
  • ciąg znaków jest tablicą,
  • konkatenacja z pomocą `+`, `+=`,
  • wycinanie elementów tablicy,
  • mnożenie stringów,
  • wyrażenia regularne jako biblioteka,
  • możliwe podejście funkcyjne,
  • `join` jako metoda łączenia znaków, `enumerate` jako zamiennik pętli,
  • pokazaliśmy, że potrafimy optymalizować swoje rozwiązanie,
  • jesteśmy w stanie zastosować różne podejścia do problemu – wyrażenia regularne, programowanie funkcyjne.

Wnioski

Czy wykazanie się tą wiedzą i umiejętnościami będzie wystarczające do zapewnienia sobie stanowiska „Junior Python Developer”? Nie. Może być przydatne. Na pewno nie zaszkodzi 😊 Trenowanie na takich podstawowych przykładach, rozwijanie się oraz dokumentowanie tego będzie dodatkowym atutem.

***

Jeśli interesuje Cię obszar rekrutacji, zajrzyj koniecznie do innych artykułów naszych ekspertów oraz do ofert pracy 🙂

5/5 ( głosy: 33)
Ocena:
5/5 ( głosy: 33)
Autor
Avatar
Sylwester Herber

Starszy Inżynier ds. oprogramowania z ponad 15-letnim doświadczeniem z różnymi technologiami – od jQuery przez Pythona, MsSql, JAVA, MongoDB, JavaScript, aż po PHP. Od 2022 roku w Sii zamienia kawę i pizzę na kod aplikacji dla klienta z branży medycznej. W wolnym czasie jeździ drezyną w mundurze polskiego kolejarza z roku 1939

Zostaw komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Może Cię również zainteresować

Pokaż więcej artykułów

Bądź na bieżąco

Zasubskrybuj naszego bloga i otrzymuj informacje o najnowszych wpisach.

Otrzymaj ofertę

Jeśli chcesz dowiedzieć się więcej na temat oferty Sii, skontaktuj się z nami.

Wyślij zapytanie Wyślij zapytanie

Natalia Competency Center Director

Get an offer

Dołącz do Sii

Znajdź idealną pracę – zapoznaj się z naszą ofertą rekrutacyjną i aplikuj.

Aplikuj Aplikuj

Paweł Process Owner

Join Sii

ZATWIERDŹ

This content is available only in one language version.
You will be redirected to home page.

Are you sure you want to leave this page?