.. _funkcje:
Funkcje
--------------------------------------
.. admonition:: W skrócie
:class: def
*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:
.. raw:: html
.. admonition:: Ć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):
.. raw:: html
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:
Deklarowanie funkcji
""""""""""""""""""""""""""""""""""
Nową funkcję deklarujemy następująco:
.. raw:: html
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 :ref:`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**.
.. _domyślne:
Funkcje wieloargumentowe i parametry domyślne
"""""""""""""""""""""""""""""""""""""""""""""""""""""""
Funkcja może przyjmować więcej niż jeden argument:
.. raw:: html
.. admonition:: Ćwiczenie 2
zmodyfikuj skrypt tak, aby funkcja obliczała wartość :math:`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
:math:`y = a x^2 + b x + c`. Jeżeli potrzebujemy tylko :math:`y = x^2`, musimy przekazać wartości ``a``, ``b`` i ``c`` odpowiednio ``1``, ``0``, ``0``. Gdyby wyrażenie :math:`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:
.. raw:: html
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.
.. raw:: html
.. _zwracanie_tupli:
.. rubric:: 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 :ref:`tuple (krotki)`. Poniższy kod zamienia współrzędne radialne punktu na kartezjańskie a wynik zwraca jako dwu-krotkę (w linii 8):
.. raw:: html
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)``
.. raw:: html
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?
.. admonition:: Uwaga
:class: def
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 :math:`n!`
.. raw:: html
**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:
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ć?
.. raw:: html
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:
.. raw:: html
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:
.. raw:: html
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.