PPy3/Funkcje: Różnice pomiędzy wersjami
(Nie pokazano 2 pośrednich wersji utworzonych przez tego samego użytkownika) | |||
Linia 12: | Linia 12: | ||
*Definicja funkcji to instrukcja złożona, podobna w strukturze do tych, które już znamy - instrukcji warunkowej i pętli. | *Definicja funkcji to instrukcja złożona, podobna w strukturze do tych, które już znamy - instrukcji warunkowej i pętli. | ||
*Wykonanie definicji funkcji ''nie wiąże się z natychmiastowym wykonaniem zawartego w niej bloku''. | *Wykonanie definicji funkcji ''nie wiąże się z natychmiastowym wykonaniem zawartego w niej bloku''. | ||
− | *W definicji funkcji (przykładowej) <tt>x</tt> i <tt>y</tt> to ''parametry formalne''; nie mają one w tym momencie określonych wartości, są miejscami do wypełnienia konkretnymi wartościami, gdy funkcję postanowimy użyć. | + | *W definicji funkcji (przykładowej) <tt>x</tt> i <tt>y</tt> to ''parametry formalne''; nie mają one w tym momencie określonych wartości, są miejscami do wypełnienia konkretnymi wartościami, gdy funkcję postanowimy użyć. Może ich być (niemal) dowolnie wiele, w tym — zero. W tym ostatnim przypadku piszemy pustą parę nawiasów. |
*Wykonanie definicji funkcji polega na nadaniu znaczenia jej nazwie, która odtąd będzie oznaczała ciąg instrukcji stanowiących wewnętrzny blok. | *Wykonanie definicji funkcji polega na nadaniu znaczenia jej nazwie, która odtąd będzie oznaczała ciąg instrukcji stanowiących wewnętrzny blok. | ||
*Użycie czyli ''wywołanie'' funkcji to wyrażenie postaci | *Użycie czyli ''wywołanie'' funkcji to wyrażenie postaci | ||
Linia 23: | Linia 23: | ||
*W definicji na ogół występuje (raz lub więcej razy) słowo (instrukcja) <tt>return</tt>. Oznacza ono, że w tym miejscu wykonanie funkcji się kończy - funkcja powraca. Wartość wyrażenia po słowie <tt>return</tt> stanowi wynik zwracany przez funkcję - czyli wartość wyrażenia, będącego wywołaniem funkcji. | *W definicji na ogół występuje (raz lub więcej razy) słowo (instrukcja) <tt>return</tt>. Oznacza ono, że w tym miejscu wykonanie funkcji się kończy - funkcja powraca. Wartość wyrażenia po słowie <tt>return</tt> stanowi wynik zwracany przez funkcję - czyli wartość wyrażenia, będącego wywołaniem funkcji. | ||
− | *Jeśli <tt>return</tt> nie ma, albo nie ma po nim wartości zwracanej, albo wykonanie funkcji kończy się ,,wypadnięciem" przez koniec bloku, wartością zwracaną jest <tt>None</tt> | + | *Jeśli <tt>return</tt> nie ma, albo nie ma po nim wartości zwracanej, albo wykonanie funkcji kończy się ,,wypadnięciem" przez koniec bloku, wartością zwracaną jest <tt>None</tt> — która w zasadzie do niczego się specjalnie nie nadaje, poza sprawdzeniem, czy mamy do czynienia z właśnie tą wartością. |
− | *''funkcja'' w Pythonie (i większości języków programowania) to coś nieco podobnego do funkcji w matematyce, ale '''to nie jest to samo pojęcie''': | + | *''funkcja'' w Pythonie (i większości języków programowania) to coś nieco podobnego do funkcji (odwzorowania) w matematyce, ale '''to nie jest to samo pojęcie''': |
**w Pythonie wywołanie funkcji może mieć skutki uboczne; | **w Pythonie wywołanie funkcji może mieć skutki uboczne; | ||
**wynik funkcji, w tym skutki uboczne, może zależeć nie tylko od argumentów wywołania. | **wynik funkcji, w tym skutki uboczne, może zależeć nie tylko od argumentów wywołania. | ||
Linia 82: | Linia 82: | ||
Funkcja może być | Funkcja może być | ||
+ | *zeroargumentowa — wówczas zarówno w definicji, jak i w wywołaniu po nazwie występuje pusta para nawiasów, | ||
*jednoargumentowa (jak powyższa <tt>podwoj</tt>), | *jednoargumentowa (jak powyższa <tt>podwoj</tt>), | ||
*dwuargumentowa (jak przykładowa), | *dwuargumentowa (jak przykładowa), | ||
Linia 95: | Linia 96: | ||
→ 3 | → 3 | ||
</source> | </source> | ||
+ | |||
+ | Parametry z wartościami domyślnymi muszą występować na końcu listy parametrów. | ||
+ | *o zmiennej liczbie argumentów, ale bez wartości domyślnych, np.: | ||
+ | |||
+ | <source lang=python> | ||
+ | def ile(*args): | ||
+ | return len(args) | ||
+ | </source> | ||
+ | |||
+ | Działa to tak: <tt>'*'</tt> postawiona przed nazwą argumentu oznacza, że wszystkie (ew. dalsze) argumenty | ||
+ | zostaną zebrane w jedną listę, która dostępna jest wewnątrz funkcji pod nazwą, jaką postawiliśmy po gwiazdce. W powyższym przykładzie, funkcja <tt>ile</tt> zwraca jako swój wynik po prostu liczbę argumentów, z jakimi funkcja została wywołana. Operator <tt>'*'</tt> nie ma tu wiele wspólnego z mnożeniem. Odróżniamy go od mnożenia po tym, że po jego lewej stronie nie stoi żaden czynnik. Ten sam operator może wystąpić również w roli poniekąd odwrotnej, np.: | ||
+ | |||
+ | <source lang=python> | ||
+ | lista = [*range(5)] | ||
+ | print(*lista) | ||
+ | → 0 1 2 3 4 | ||
+ | </source> | ||
+ | |||
+ | W pierwszej linii, gwiazdka niejako ,,rozwija" sekwencję <tt>range(5)</tt> w ciąg wartości (kolejnych liczb) wstawionych pod operator <tt>[...]</tt>; w linii drugiej, <tt>lista</tt> jest również ,,rozpakowana" w | ||
+ | ciąg oddzielnych argumentów wywołania funkcji <tt>print</tt> — sprawdź, że wynik <tt>print(lista)</tt> wyglądałby nieco inaczej. | ||
=Ćwiczenia= | =Ćwiczenia= | ||
− | + | 1. Napisz funkcję <tt>pierwiastki(a, b, c)</tt>, która wypisuje na ekran (za pomocą wywołania <tt>print</tt>) pierwiastki równania kwadratowego o wspołczynnikach ''a'', ''b'' i ''c'': <tt>a * x**2 + b * x + c == 0</tt>. W przypadku, gdy pierwiastków nie ma, wypisuje komunikat ''Równanie nie ma pierwiastków.'' W przypadku, gdy ''a=0'', wypisuje komunikat ''Błędne dane: a==0'' i kończy działanie. | |
− | + | ||
− | + | 2. Napisz analogiczną funkcję, która zamiast bezpośrednio wypisywać rozwiązania, zwraca je jako wynik swojego działania. Wypisaniem ich na ekran zajmuje się odrębna instrukcja. W przypadku, gdy pierwiastków nie ma, zwraca wartość <tt>None</tt>. Nie sprawdzaj warunku ''a!=0'' - co się dzieje, gdy nie jest spełniony przez argumenty wywołania funkcji? | |
− | + | ||
− | + | 3. Napisz funkcję <tt>silnia(n)</tt>, która oblicza i zwraca wartość silni: <tt>n! := 1 * 2 * 3 * ... (n - 1) * n</tt>. Uwzględnij, że przyjmuje się że <tt>0! = 1</tt>, natomiast dla liczb ujemnych silnia nie jest określona - co można zasygnalizować zwracając jako wynik <tt>None</tt>. Użyj pętli <tt>for</tt> i funkcji <tt>range</tt>. | |
− | + | ||
− | + | 4. Funkcję silnia można zdefiniować w sposób rekurencyjny - za pomocą następujących warunków: | |
+ | <blockquote> | ||
+ | <tt>n! == None</tt> dla <tt>n < 0</tt><br> | ||
+ | <tt>0! == 1</tt><br> | ||
+ | <tt>n! == n * (n - 1)!</tt> | ||
+ | </blockquote> | ||
Napisz kod funkcji <tt>silnia(n)</tt> realizujący powyższą definicję. Porównaj działanie tego kodu z realizacją tej funkcji z poprzedniego przykładu dla dużych wartości <tt>n</tt>. | Napisz kod funkcji <tt>silnia(n)</tt> realizujący powyższą definicję. Porównaj działanie tego kodu z realizacją tej funkcji z poprzedniego przykładu dla dużych wartości <tt>n</tt>. | ||
Aktualna wersja na dzień 13:32, 10 maj 2023
Funkcje
Gdy raz wymyślimy jakiś sprytny algorytm, to nie ma co go za każdym razem odkrywać na nowo. Żeby było łatwiej pisać raz, a korzystać wielokrotnie - wymyślono funkcje.
Definiowanie funkcji
def funkcja(x, y):
BLOK INSTRUKCJI
- Definicja funkcji to instrukcja złożona, podobna w strukturze do tych, które już znamy - instrukcji warunkowej i pętli.
- Wykonanie definicji funkcji nie wiąże się z natychmiastowym wykonaniem zawartego w niej bloku.
- W definicji funkcji (przykładowej) x i y to parametry formalne; nie mają one w tym momencie określonych wartości, są miejscami do wypełnienia konkretnymi wartościami, gdy funkcję postanowimy użyć. Może ich być (niemal) dowolnie wiele, w tym — zero. W tym ostatnim przypadku piszemy pustą parę nawiasów.
- Wykonanie definicji funkcji polega na nadaniu znaczenia jej nazwie, która odtąd będzie oznaczała ciąg instrukcji stanowiących wewnętrzny blok.
- Użycie czyli wywołanie funkcji to wyrażenie postaci
funkcja(x, y)
gdzie x i y mają już konkretne wartości (w tym miejscu mogą stać również wyrażenia złożone), które będą użyte w instrukcjach stanowiących definicję.
- W definicji na ogół występuje (raz lub więcej razy) słowo (instrukcja) return. Oznacza ono, że w tym miejscu wykonanie funkcji się kończy - funkcja powraca. Wartość wyrażenia po słowie return stanowi wynik zwracany przez funkcję - czyli wartość wyrażenia, będącego wywołaniem funkcji.
- Jeśli return nie ma, albo nie ma po nim wartości zwracanej, albo wykonanie funkcji kończy się ,,wypadnięciem" przez koniec bloku, wartością zwracaną jest None — która w zasadzie do niczego się specjalnie nie nadaje, poza sprawdzeniem, czy mamy do czynienia z właśnie tą wartością.
- funkcja w Pythonie (i większości języków programowania) to coś nieco podobnego do funkcji (odwzorowania) w matematyce, ale to nie jest to samo pojęcie:
- w Pythonie wywołanie funkcji może mieć skutki uboczne;
- wynik funkcji, w tym skutki uboczne, może zależeć nie tylko od argumentów wywołania.
Skutkami ubocznymi wywołania nazywamy jakiekolwiek efekty tej instrukcji, które nie sprowadzają się do wartości zwracanej. A więc, zmianę wartości innych zmiennych, czy np. wysłanie jakichś danych siecią, lub wypisanie czegoś na ekran.
- Funkcja może być wartością zmiennej; inaczej mówiąc, funkcja może być znana pod dodatkowymi nazwami (aliasami), może być włożona do listy na którąś z jej pozycji itp.
ff = funkcja
...
wynik = ff(x)
- ,,Oryginalna" nazwa funkcji, czyli występująca w jej definicji, jest jednak w pewnym sensie uprzywilejowana.
- Zmienne (nazwy) powołane do życia wewnątrz funkcji są lokalne; ich wartości ,,żyją" tylko póki wykonuje się funkcja.
- Zmienne lokalne powielające nazwy ,,zewnętrzne" względem funkcji przesłaniają te zewnętrzne. Chyba, że użyjemy deklaracji global:
x = 1
def podwojx(): # to nie zadziała:
x = x * 2
podwojx()
UnboundLocalError Traceback (most recent call last)
<ipython-input-3-6edd575e5cda> in <module>()
----> 1 podwojx()
<ipython-input-1-1da635f42ffa> in podwojx()
1 def podwojx():
----> 2 x *= 2
3
UnboundLocalError: local variable 'x' referenced before assignment
Jak widzimy, komunikat o błędzie pojawił się dopiero w wyniku wywołania błędnej funkcji.
x = 1
def podwojx(): # to zadziała - co nie znaczy, że jest mądre...
global x
x = x * 2
podwojx()
print x
→ 2
Należy unikać takich sztuczek, jak powyżej; jeśli już, to:
def podwoj(x):
return 2 * x
liczba = 1
print podwoj(liczba)
→ 2
Wskazówka: każde użycie deklaracji global w programie powinno być dobrze uzasadnione. Jeżeli używamy jej więcej niż sporadycznie, to coś jest nie tak.
Funkcja może być
- zeroargumentowa — wówczas zarówno w definicji, jak i w wywołaniu po nazwie występuje pusta para nawiasów,
- jednoargumentowa (jak powyższa podwoj),
- dwuargumentowa (jak przykładowa),
- trzy-, cztero-, ... itd.
- o zmiennej liczbie argumentów, z wartościami domyślnymi dla brakujących:
def dodaj(x, y=1):
return x + y
dodaj(2, 3)
→ 5
dodaj(2)
→ 3
Parametry z wartościami domyślnymi muszą występować na końcu listy parametrów.
- o zmiennej liczbie argumentów, ale bez wartości domyślnych, np.:
def ile(*args):
return len(args)
Działa to tak: '*' postawiona przed nazwą argumentu oznacza, że wszystkie (ew. dalsze) argumenty zostaną zebrane w jedną listę, która dostępna jest wewnątrz funkcji pod nazwą, jaką postawiliśmy po gwiazdce. W powyższym przykładzie, funkcja ile zwraca jako swój wynik po prostu liczbę argumentów, z jakimi funkcja została wywołana. Operator '*' nie ma tu wiele wspólnego z mnożeniem. Odróżniamy go od mnożenia po tym, że po jego lewej stronie nie stoi żaden czynnik. Ten sam operator może wystąpić również w roli poniekąd odwrotnej, np.:
lista = [*range(5)]
print(*lista)
→ 0 1 2 3 4
W pierwszej linii, gwiazdka niejako ,,rozwija" sekwencję range(5) w ciąg wartości (kolejnych liczb) wstawionych pod operator [...]; w linii drugiej, lista jest również ,,rozpakowana" w ciąg oddzielnych argumentów wywołania funkcji print — sprawdź, że wynik print(lista) wyglądałby nieco inaczej.
Ćwiczenia
1. Napisz funkcję pierwiastki(a, b, c), która wypisuje na ekran (za pomocą wywołania print) pierwiastki równania kwadratowego o wspołczynnikach a, b i c: a * x**2 + b * x + c == 0. W przypadku, gdy pierwiastków nie ma, wypisuje komunikat Równanie nie ma pierwiastków. W przypadku, gdy a=0, wypisuje komunikat Błędne dane: a==0 i kończy działanie.
2. Napisz analogiczną funkcję, która zamiast bezpośrednio wypisywać rozwiązania, zwraca je jako wynik swojego działania. Wypisaniem ich na ekran zajmuje się odrębna instrukcja. W przypadku, gdy pierwiastków nie ma, zwraca wartość None. Nie sprawdzaj warunku a!=0 - co się dzieje, gdy nie jest spełniony przez argumenty wywołania funkcji?
3. Napisz funkcję silnia(n), która oblicza i zwraca wartość silni: n! := 1 * 2 * 3 * ... (n - 1) * n. Uwzględnij, że przyjmuje się że 0! = 1, natomiast dla liczb ujemnych silnia nie jest określona - co można zasygnalizować zwracając jako wynik None. Użyj pętli for i funkcji range.
4. Funkcję silnia można zdefiniować w sposób rekurencyjny - za pomocą następujących warunków:
n! == None dla n < 0
0! == 1
n! == n * (n - 1)!
Napisz kod funkcji silnia(n) realizujący powyższą definicję. Porównaj działanie tego kodu z realizacją tej funkcji z poprzedniego przykładu dla dużych wartości n.