Archiwa kategorii: Moduł 2 kursu

M2 L11 CZĘŚĆ PRAKTYCZNA ZADANIA

Część praktyczna

Zadania

  1. Zbuduj funkcję sprawdzająca czy dana liczba jest ułamkiem.
  2. Zbuduj funkcję sumującą wszystkie elementy w tablicy liczb o dowolnej niezerowej długości.
  3. Zbuduj funkcję znajdującą najmniejszą liczbę w tablicy.
  4. Stwórz funkcję przyjmujące jako argument 2 tablice, zsumuj wartości poszczególnych elementów danej tablicy, a następnie podziel te liczby przez siebie. Zwróć uwagę na dzielenie przez zero.
  5. Sprawdź czy zadany rok jest przestępny (podzielny przez 4 i nie podzielny przez 100, lub podzielny przez 400)
  6. Zbuduj funkcję potęgowania – dla ułatwienia załóż, że potęga jest liczbą całkowitą i nieujemną. Pamiętaj o potędze 0.
  7. Stwórz funkcję liczącą silnie danej wartości (uwaga – stosuj tylko małe liczby)
  8. Stwórz zagnieżdżoną listę odpowiadającą grze w kółko i krzyżyk. Załóż, że wartość 0 oznacza puste pole, 1 to krzyżyk a 2 – kółko. Utwórz funkcję dodajKrzyzyk oraz dodajKolko, które pozwolą przeprowadzić rozgrywkę.
  9. W powyższych funkcjach dodaj sprawdzanie czy dane pole jest już zajęte i wyświetlanie stosownego komunikatu.
  10. Zapewnij, by obie funkcje były wywoływane w naprzemiennej kolejności, włączając w to sytuacje w których dany ruch był błędny (jak w 9 ) i musi zostać powtórzony.
  11. Ogranicz maksymalną ilość ruchów do 9, wyświetlając inny komunikat w razie próby przekroczenia.

M2 L10 Podsumowanie

Podsumowanie

Zapoznaliśmy się z kolejnym kluczowym elementem programowania jakim są funkcje. Wprawne ich stosowanie jest niezbędne do pisania bardziej złożonych programów, mogących operować na dynamicznych danych, a nie tylko korzystać z wpisanych na sztywno zmiennych. Pozwala nam stworzyć kod, który będzie mógł być wykorzystany przez innych użytkowników i operować na ich danych, dając poprawne rezultaty.

Przedstawiliśmy również pojęcie algorytmu – które zapewne stosowaliśmy już w praktyce, nie zdając sobie z tego sprawy. Rozbijanie bardziej złożonych problemów na prostsze części pozwala uniknąć „zakopania się” w skomplikowanym kodzie. Jasne zdefiniowanie niezbędnych kroków pozwoli nam zrealizować zadania, które na pierwszy rzut oka wydają się znacznie trudniejsze niż są w rzeczywistości.

Część teoretyczna była zauważalnie krótsza, gdyż funkcje wykorzystują znane nam już instrukcje i typy danych oraz polegają na samodzielnym myśleniu i analizie problemu. W celu opanowania tematu, konieczna jest praktyka.

Podobnie jak poprzednio, przedstawimy teraz kilka zadań wymagających utworzenia funkcji realizujących konkretne cele. Poziom komplikacji będzie nieco wyższy niż w poprzednim module. Być może przydatne będzie najpierw stworzenie algorytmu i podział problemu na prostsze części.

M2 L9 Algorytmy

Algorytmy

Przedstawione do tej pory zadania były dość proste, składające się z maksymalnie kilku instrukcji. Realne problemy programistyczne są jednak bardziej złożone i wymagają zwykle ułożenia planu działania, czyli tzw. algorytmu.

W uproszczeniu, algorytmy są przepisem na wykonanie zadanego nam problemu – ciągiem przyczynowo skutkowym, który ma doprowadzić nas do końcowego celu. Określamy kolejne kroki naszego programu, sprawdzamy, czy mają logiczny sens i dopiero wtedy tworzymy faktyczny program.

Pisanie kodu na tzw. „żywioł” ma swoje zalety, jednak złożone problemy programistyczne lepiej jest rozłożyć na prostsze elementy i skupić się na realizacji każdego z nich. Bez tego, dość łatwo można pominąć pozornie trywialne kroki lub popełnić proste błędy logiczne.

W najprostszej postaci algorytm tworzymy „w głowie”, ale możemy go zapisać w postaci tekstowej, korzystając z pseudokodu lub zwykłego języka. Istnieją wprawdzie bardziej zaawansowane techniki i specjalne diagramy, jednak dla początkujących wystarczy zwykły tekst.

Przykładowy algorytm: Utwórz funkcje zwracającą większą z dwóch liczb.

Krok 1 – Utwórz funkcję przyjmującą dwa parametry A i B

Krok 2 – Porównaj parametry

Krok 3 – Jeśli A=B, zwróć A.

Krok 4 – Jeśli A!=B, sprawdź czy A > B

Krok 5 – Jeśli tak, zwróć A

Krok 6 – W przeciwnym razie, zwróć B

Jest to bardzo prosty algorytm i przełożenie go na Python nie powinno stanowić żadnej trudności. Zauważmy jednak, iż krok trzeci wcale nie był oczywisty i przystępując bezpośrednio do pisania kodu można było łatwo o nim zapomnieć. Im bardziej złożony problem, tym lepiej jest najpierw rozplanować sobie jego działanie. Unikniemy w ten sposób tworzenia kodu, który i tak później uznamy za wadliwy.

M2 L8 Testowanie funkcji

Testowanie funkcji

Po zakończeniu tworzenia funkcji powinniśmy przetestować ją dla kilku zadanych zestawów parametrów. Istnieją do tego specjalistyczne narzędzia, jednak sami możemy stworzyć najprostszy możliwy test.

W tym celu tworzymy dwie zmienne: pierwsza z nich zawiera spodziewany wynik działania naszej funkcji, czyli to co powinniśmy uzyskać przy prawidłowym działaniu. Jej wartość wyliczamy „ręcznie”, tak by mieć pewność, że zawsze jest poprawna. Do drugiej zmiennej przypisujemy wynik zwracany przez funkcję. Następnie porównujemy oba wyniki.

Biorąc za przykład poprzednio zdefiniowaną funkcję poleProstokata:

Oczywiście taki test należy przeprowadzić dla wielu zestawów parametrów, włączając w to sytuacje skrajne, np. zero, liczby ujemne, itd. Naturalnie w takim przypadku należy również dostosować spodziewany wynik, wstawiając tam np. tekst o błędnych parametrach, zerowym polu itd. – wszystkie scenariusze, które powinniśmy ująć w ciele naszej funkcji.

Profesjonalne testy korzystają ze specjalnych kodów błędów wbudowanych w język programowania, np. ostrzegających o dzieleniu przez zero, czy wyjściu poza zakres tablicy. Są to jednak bardzo złożone zagadnienia – na potrzeby obecnego kursu, w zupełności wystarczą nam stosowne napisy na ekranie.

M2 L7 Funkcje – zmienne lokalne a globalne

Funkcje – zmienne lokalne a globalne

W poprzednim przykładzie korzystaliśmy ze zmiennej występującej poza ciałem funkcji i nie będącej parametrem. W sytuacji gdy chcemy tylko odczytać jej wartość, działanie takie będzie zaakceptowane przez Python. Jeśli jednak zechcemy dokonać jej modyfikacji, otrzymamy błąd:

Jeśli w ciele funkcji pojawiają się przypisania do zmiennych, Python (i większość innych języków programowania) potraktuje je jako zmienne lokalne, właściwe tylko dla tej funkcji. Nie mają one nic wspónego z wartością zmiennych występujących poza funkcją, np.

Wydawać by się mogło, że wynikiem działania takiego programu będzie „20” w obu przypadkach – taką wartość ustawiliśmy w funkcji i taka powinna być poza nią. Tymczasem zmienna X w ciele funkcji i na zewnątrz to dwa niezależne od siebie byty. X wewnątrz funkcji jest tzw. zmienną lokalną, aktywną tylko w jej ciele – wszelkie zmiany nie będą odzwierciedlone na zewnątrz, nawet jeśli nazwa zmiennej jest taka sama.

Jeśli chcemy odwołać się do X spoza funkcji, musimy zaznaczyć iż chodzi nam o zmienną globalną, która obejmie swoim zasięgiem cały program, a nie tylko ciało funkcji. Służy do tego słowo kluczowe global użyte wewnątrz funkcji

Wyjątkiem jest tutaj sytuacja, w których w ciele funkcji przypisujemy wartość do konkretnego indeksu listy, np. dzienTygodnia[2]=’środa’. Ponieważ nie tworzymy w ten sposób nowej listy – jak pamiętamy, służyła do tego konstrukcja typu lista=[zmienna1,zmienna2,zmienna3] – to Python traktuje to jako odwołanie do zmiennej zewnętrznej i nie wymaga użycia global.

Bardziej zaawansowani programiści zwykle starają się unikać zmiennych globalnych, gdyż mogą one skutkować szeregiem różnych problemów, szczególnie w bardziej złożonych projektach. Dla tej samej funkcjonalności używa się dodatkowych funkcji odczytujących i przypisujących wartości do zmiennych zewnętrznych. Na potrzeby szkoleniowe używanie global jest jednak zupełnie wystarczające.

M2 L6 Q Funkcje – reguły tworzenia

Funkcje – reguły tworzenia

PYTANIE 1 Z 1

Inną ważną zasadą tworzenia funkcji jest unikanie stosowania zmiennych, które nie są parametrami. Nie jest to wprawdzie warunek niezbędny do poprawnego działania funkcji, ale mogący spowodować problemy. Spójrzmy na poniższy kod:

Jak myślisz, jakie problemy może spowodować taki program:

Wybierz WSZYSTKIE prawidłowe odpowiedzi.

A

Funkcja przestanie działać, jeśli usuniemy X

B

Funkcja przestanie działać, jeśli przeniesiemy X na koniec

C

Funkcja może działać w różny sposób dla tego samego parametru a, w zależności od x

M2 L5 Funkcje – reguły tworzenia

Funkcje – reguły tworzenia

Podstawową regułą przy tworzeniu funkcji jest „nie można użyć funkcji zanim zostanie ona zdefiniowana”. Jest to dość oczywiste – kod programu wykonywany jest od góry do dołu, więc jeśli w pierwszej linijce próbujemy korzystać z czegoś, co zostanie utworzone dopiero w linii 10, otrzymamy błąd. Z tego też powodu przyjęło się umieszczać definicje funkcji na początku kodu.

Co więcej, często chcemy wypisać wszystkie (w miarę możliwości) funkcje zanim dokładnie zdefiniujemy ich działanie. Pozwoli to szybko ocenić, czy mamy już wszystko co potrzebujemy. W tym celu tworzymy tzw. puste metody, używając słowa kluczowego pass.

Inne języki programowania realizują to poprzez nawiasy klamrowe bez żadnej zawartości między nimi, np.

function nowaFunkcja(){}

function kolejnaFunkcja(){}

Mając tak rozplanowaną listę funkcji, możemy następnie przystąpić do wypełniania ich odpowiednimi operacjami. Działanie takie nie jest w 100% wymagane – można wszystkie funkcje pisać „z marszu” – ale czasem możemy „zaciąć się” na trudnej funkcji i zapomnieć o utworzeniu pozostałych.

M2 L4 Funkcje – obsługa błędów

Funkcje – obsługa błędów

W poprzednim zadaniu chcieliśmy by funkcja zwracała wartość 0, jeśli dostarczone parametry były nieprawidłowe. Zwracanie 0, -1 lub innych wartości, które nie powinny normalnie wystąpić dla danej operacji jest prostym sposobem zaznaczenia, iż „coś nie zadziałało”.

Zapobiega to jednocześnie zatrzymaniu programu, np. jeśli przypiszemy wynik działania funkcji do zmiennej, a następnie zechcemy wykonać na niej jakieś operacje. Wyniki nie będą prawidłowe, ale program nadal się wykona – a komunikat o błędzie powinien pozwolić nam zorientować się, że powinniśmy coś poprawić w doborze parametrów

Prawdziwa obsługa błędów jest znacznie bardziej złożonym procesem, wymagającym dobrej znajomości programowania, umiejętności przewidywania skrajnych scenariuszy oraz tworzenia stosownych obejść. Dla początkujących, zupełnie wystarczy przedstawiony wcześniej sposób.

M2 L3 Funkcje – ciąg dalszy

Tworzenie funkcji może wydawać się proste, ale stworzenie „dobrych” funkcji to umiejętność znacznie trudniejsza do opanowania. Funkcje używane w praktycznym programowaniu będą znacznie dłuższe, odnosiły się do innych funkcji, miały złożone listy parametrów itd.

Dobre funkcje muszą być jednocześnie proste – umożliwiając zrozumienie zasad działania „na pierwszy rzut oka” – jak też i na tyle złożone, by działać poprawnie dla różnych kombinacji parametrów. Prosty przykład:

Programistycznie, funkcja ta jest zupełnie poprawna. Podając długości dwóch boków prostokąta otrzymamy ich iloczyn, czyli pole. Co jednak w sytuacji, gdy jedna z długości będzie ujemna albo zerowa? Dwie liczby ujemne? Wynik zostanie wyliczony zgodnie z zasadami matematyki, nie będzie jednak miał wiele wspólnego z polem prostokąta.

Z pomocą przychodzą znane nam już instrukcje warunkowe. Definiując funkcje, możemy dodawać dowolne warunki logiczne i uzależniać od nich zwracaną wartość.

UWAGA: Wiele języków programowania wymaga określenia typu parametrów dostarczanych do funkcji, jak również tego co będzie zwracane przez return, np. liczba poleProstokata(liczba a, liczba b). W ramach kursu zakładamy jednak, że dostarczone parametry będą miały poprawny typ, a ewentualne błędy będą wynikać z ich wartości.

Zadania

1. Zmodyfikuj funkcję poleProstokąta, by wypisać komunikat o błędzie dla niewłaściwych wartości liczbowych i zwrócić dla nich 0.

2. Zmodyfikuj funkcje poleProstokata, by nie zwracała żadnej wartości w przypadku ujemnych parametrów. Następnie przypisz jej wynik do zmiennej i wykonać na niej dowolną operację matematyczną.