Funkcje

W skrócie

Funkcja to autonomiczny fragment programu, wydzielony do późniejszego wielokrotnego wykorzystania.

Funkcje w programowaniu przypominają te znane z matematyki: najczęściej przyjmują argumenty i zwracają jakąś wartość. Funkcję wywołujemy wpisując po prostu jej nazwę, a argumenty wymieniamy w nawiasach. Dokładnie w ten sposób, w jaki korzystasz z funkcji print(). Tu dla przykładu pokażmy Pythonową funkcję sinus:

Ćwiczenie 1

zmodyfikuj skrypt tak, aby obliczyć cos(2.4)

Raz zadeklarowane funkcje, np. trygonometryczne mogą być wielokrotnie wykorzystywane bez konieczności ich ponownego deklarowania, np. jak w poniższym programie, który obraca wektor w przestrzeni trójwymiarowej wokół osi Z (współrzędna z pozostaje bez zmian podczas tego obrotu):

Biblioteka języka Python, dostarczona wraz z interpreterem, udostępnia szeroki wachlarz funkcji na różne okazje, m.in. funkcje matematyczne. Trzeba je tylko znaleźć (patrz dokumentacja online) i zaimportować na początku programu odpowiedni moduł. A jak możemy stworzyć własne funkcje?

Deklarowanie funkcji

Nową funkcję deklarujemy następująco:

Należy wykorzystać słowo kluczowe def, po którym następuje nazwa funkcji i lista argumentów (w nawiasach). Tu funkcja ma tylko jeden argument - x. Jak zwykle w Pythonie, tu też panuje zasada wcięcia : w funkcji są te instrukcje, które są odpowiednio przesunięte (linie 3 i 4) względem tu nagłówka funkcji (linia 1). Szególnie ważna jest linia 4, która odpowiada za to, że funkcja zwraca wartość, korzystająca ze słowa kluczowego return. Wynikiem powyższego programu będzie oczywiście liczba 25.

Funkcje wieloargumentowe i parametry domyślne

Funkcja może przyjmować więcej niż jeden argument:

Ćwiczenie 2

zmodyfikuj skrypt tak, aby funkcja obliczała wartość \(y = a x^3 + b x^2 + c x + d\).

Wywołując funkcję musimy podać wartości wszystkich parametrów, oddzielając je przecinkami, nawet jeżeli ich nie potrzebujemy. W powyższym przykładzie funkcja_kwadratowa oblicza wartość wyrażenia \(y = a x^2 + b x + c\). Jeżeli potrzebujemy tylko \(y = x^2\), musimy przekazać wartości a, b i c odpowiednio 1, 0, 0. Gdyby wyrażenie \(y = x^2\) było tym, którego potrzebujemy najczęściej, wpisywanie tych zer i jedynki zaczęło by już nas męczyć. I tu z pomocą przychodzą wartości domyślne parametrów:

Linijki 6 i 7 są w tym przykładzie równoważne.

Co ciekawe, ani przyjmowanie argumentów ani zwracanie wartości nie jest obowiązkowe! W tym pierwszym przypadku lista argumentów funkcji jest pusta, w drugim zaś w definicji funkcji nie ma instrukcji return, np.

Zwracanie wielu wyników

Pewnym ograniczeniem jest jednak to, że funkcja może zwrócić tylko jedną wartość. Co robić, kiedy musimy zwrócić np. dwie liczby? Wykorzystujemy w tym celu struktury danych; najbardziej nadają się do tego celu tuple (krotki). Poniższy kod zamienia współrzędne radialne punktu na kartezjańskie a wynik zwraca jako dwu-krotkę (w linii 8):

Zauważ, że w linii 13 następuje rozpakowanie tupli. Program wygląda dzięki temu tak, jakby funkcja radial_to_cartesian() naprawdę zwracała dwie wartości a nie jedną!

Powyższa funkcja zakłada, że środek układu współrzędnych, w którym zmierzono kąt, leży w (0,0). Czasem chcielibyśmy to zmienić. Przyda się zatem funkcja, która dodatkowo przyjmuje również współrzędne środka. Ponieważ jednak nie będzie to często wykorzystywana opcja, pozostawiamy wartość domyślną na (0,0)

Widać tu główną zaletę stosowania argumentów funkcji z wartościamy domyślnymi: nowa funkcja radial_to_cartesian() wywołana po staremu, a więc bez podawania współrzędnych środka, działa dokładnie tak jak działała do tej pory.

Rekurencja

W programie losującym procenty funkcja losuj_procenty() wywołuje funkcję random(). Ciąg wywołań może być znacznie dłuższy: funkcja może wywoływać funkcję, która wywołuje funkcję która wywołuje … itd. Czy jednak funkcja może wywoływać samą siebie?

Uwaga

Wywoływanie funkcji przez samą siebie nazywamy rekurencją.

Wykorzystując rekurencję w swoich programach należy uważać, aby program nie działał w nieskończoność. W poniższym przykładzie funkcja silnia(n) oblicza \(n!\)

Zalety rekurencji

  • zastosowanie rekurencji często bardzo upraszcza i skraca kod

  • skomplikowany problem może być rekurencyjnie podzielony na prostsze

  • doskonale pasuje do niektórych algorytmów, np przechodzenia grafu albo drzewa; zastosowanie iteracji jest znacznie bardziej skomplikowane

Rekurencja ma też wady

  • wywołania rekurencyjne są bardziej kosztowne obliczeniowo

  • łatwo popełnić błąd w którym program działa w nieskończoność, co skutkuje przepełnieniem stosu (ang. stack overflow)

  • kod rekurencyjny trudniej prześledzić

Zmienne lokalne i globalne

W programie często wykorzystujemy zmienne. Zmienne możemy też tworzyć wewnątrz funkcji. Co się stanie, kiedy nazwy zmiennych będą się powtarzać?

Powyższy przykład pokazuje, że zmienne stworzone wewnątrz i na zewnątrz funkcji to dwie zupełnie różne sprawy. Mamy tu bowiem dwie różne zmienne o nazwie s. Jedna jest widziana w programie głównym, druga zaś - tylko w funkcji. I tu pojawia się pytanie, czy w funkcji można zapisać coś do zmiennej globalnej? Otóż można, trzeba tylko skorzystać ze słowa kluczowego global, jak w programie poniżej:

Jakie są losy zmiennej global_x? Na samym początku programu jest tworzona z wartością początkową 0. Następnie wywoływana jest funkcja ustaw_globalna(), która zmienia jej wartość na 1. Funkcja drukuj_globalna2() tworzy jednak własną lokalną zmienną o tej samej nazwie, przesłaniając tym samym zmienną globalną. Wartość zmiennej globalnej pozostanie niezmieniona, mimo że na ekranie wydrukowana zostanie wartość 4.

Więcej o funkcjach

Funkcja w Pythonie jest pewnym obiektem, przypisanym do jego nazwy, np. radial_to_cartesian() zdefiniowana w jednym z powyższych przykładów. Funkcja zatem, zupełnie jak inne obiekty, może być argumentem funkcji! Do czego to może się przydać? Możesz na przykład zaimplementować całkowanie numeryczne funkcji metodą trapezów:

Funkcję podcałkową można oczywiście wpisać wewnątrz funkcji trapezoidal(), takie rozwiązanie byłoby mało ogólne. Jak zatem umożliwić całkowanie dowolnej funkcji? Należy przekazać ją jako argument funkcji trapezoidal(), jak w powyższym przykładzie.