====== Funkcje ====== Funkcja to wydzielony fragment programu, który posiada unikatową nazwę, ma określone argumenty wejściowe oraz zwraca pojedynczą wartość. Możemy definiować własne funkcje, które mogą być następnie wielokrotnie użyte w programie, realizując kod zawarty w definicji funkcji z różnymi wartościami argumentów. Przykład **definicji funkcji** z jednym argumentem: float kwadrat(float x) { return x * x; } Powyższy kod zawiera definicję funkcji o nazwie ''kwadrat'', która przyjmuje jeden argument typu ''float'' o nazwie ''x''. Nagłówek definicji zawiera również informację o typie wartości zwracanej - w tym wypadku jest to również ''float''. Funkcja podnosi do kwadratu wartość daną w argumencie ''x''. Instrukcja ''return'' przerywa działanie funkcji i zwraca wartość iloczynu '' x*x'' Użycie tak zdefiniowanej funkcji wygląda w następujący sposób: x = kwadrat(42); printf("%f\n", kwadrat(x)); y = kwadrat(kwadrat(2)); Ważne: definicja funkcji musi pojawić się przed jej pierwszym użyciem, więc najlepszym miejscem na zdefiniowanie funkcji jest początek pliku (powyżej funkcji ''main()''). Funkcje mogą posiadać większą liczbę argumentów. Dla każdego argumentu należy określić typ i nazwę zmiennej: void suma(int x, int y, int z) { printf("%d\n", x + y + z); } Zwróć uwagę, że funkcja nie zawsze zwraca wartość (zobacz powyższy przykład, gdzie brakuje instrukcji ''return''), wówczas w nagłówku definicji funkcji typ zwracany to ''void''. ===== Przykład: definicja funkcji i jej użycie ===== Poniżej znajduje się pełny przykład demonstrujący definicję funkcji o nazwie ''silnia'', która służy do wyznaczania wartości silni z liczby całkowitej: #include /* definicja funkcji */ int silnia(int n) { int i=2; int s=1; while( i < n+1 ) { s = s * i; i = i + 1; } return s; } int main() { int liczba, s; printf("Podaj liczbe: "); scanf("%d", &liczba); s = silnia(liczba); printf("Silnia z %d wynosi %d\n",liczba,s); return 0; } Źródło: {{zajecia:pp1_2020_1:silnia1.c}} ===== Zmienne globalne i lokalne ===== Zmienne deklarowane wewnątrz funkcji są zmiennymi **lokalnymi**, tzn. ich zakres //życia// jest ograniczone zasięgiem funkcji. Istnieje możliwość deklarowania zmiennych poza zasięgiem funkcji, wówczas są to zmienne **globalne**. Zmienne globalne są dostępne przez każdą funkcję, ich zakres //życia// rozciąga się na cały plik programu. \\ Przykład użycia zmiennych globalnych i lokalnych: #include /* zmienna globalna */ int globalna; void f(void) { /* zmienna lokalna */ int lokalna = 6; globalna++; printf("wewnatrz funkcji: globalna = %d\tlokalna = %d\n",globalna,lokalna); } int main() { /* zmienna lokalna */ int lokalna; printf("globalna = %d\tlokalna = %d\n",globalna,lokalna); f(); printf("globalna = %d\tlokalna = %d\n",globalna,lokalna); return 0; } Źródło: {{zajecia:pp1_2020_1:globalna.c}} Dobrą praktyką jest tworzenie funkcji w taki sposób, aby nie korzystały ze zmiennych globalnych. Taka funkcja realizuje wyizolowany fragment kodu (algorytym), którego działanie zależy wyłącznie od wartości argumentów funkcji a jej działanie nie wpływa na stan programu (nie zmienia wartości żadnych zmiennych spoza zakresu funkcji). Pozwala to utrzymać spójną strukturę kodu i ułatwia rozwój kodu, jego analizę i przyśpiesza wyszukiwanie błędów. \\ Kiedy działanie funkcji uzależnione jest od zmiennych globalnych, które mogą być modyfikowane w dowolnym miejscu programu, powoduje to znaczne utrudnienie interpretacji działania programu, gmatwa kod i utrudnia wyszukiwanie błędów. Na zajęciach obowiązuje **zakaz używania zmiennych globalnych** ! ==== Ćwiczenie: Największy wspólny dzielnik (NWD) ==== Zaimplementuj funkcję o nazwie ''nwd'', która wyznaczy największy wspólny dzielnik za pomocą algorytmu Euklidesa. \\ Funkcja realizuje następującą specyfikację:\\ **Argumenty funkcji**: dwie liczby całkowite ''a'' i ''b''\\ **Wartość zwracana z funkcji**: wartość całkowita będąca największym wspólnym dzielnikiem liczb ''a'' i ''b'' **Algorytm Euklidesa **: dla danych liczb całkowitych dodatnich ''a'' i ''b'' wykonuj: - jeżeli ''a'' jest równe ''b'' to wartość ''a'' jest największym wspólnym dzielnikiem i zakończ algorytm - jeżeli ''a > b'' to zastąp ''a'' wartością ''a-b'' i wróć do punktu 1 - jeżeli ''a < b'' to zastąp ''b'' wartością ''b-a'' i wróć do punktu 1 Napisz program, który korzystając z funkcji ''nwd'' wyznaczy największy wspólny dzielnik dwóch dowolnych liczb całkowitych podanych przez użytkownika. **Przykład działania programu** Podaj dwie liczby calkowite: 20 15 nwd=5 Podaj dwie liczby calkowite: 144 233 nwd=1 Podaj dwie liczby calkowite: 4673826832 47382974392 nwd=8 ===== Rekurencja ===== Funkcje, które wołają same siebie nazywamy funkcjami rekurencyjnymi. Przykład funkcji rekurencyjnej: int rsilnia(int n) { if(n <= 1) return 1; return n*rsilnia(n-1); } Źródło: {{zajecia:pp1_2020_1:silnia2.c}} Każdy algorytm wykorzystujący funkcję rekurencyjną, można wyrazić w postaci iteracyjnej. int silnia(int n) { int i, s=1; for(i=1; i < n+1 ; i++) s*= i; return s; } ==== Ćwiczenie: NWD rekurencyjnie ==== Zaimplementuj funkcję o nazwie ''nwd2'', która wyznaczy największy wspólny dzielnik za pomocą algorytmu Euklidesa w sposób rekurencyjny. Specyfikacja funkcji oraz działanie programu powinny byc identyczne jak w poprzednim ćwiczeniu, jedyną różnicą jest zastosowanie rekurencji. \\ **Algorytm Euklidesa ** w formie rekurencyjnej przybiera postać: - jeżeli ''a'' jest równe ''b'' to wartość ''a'' jest największym wspólnym dzielnikiem i zakończ algorytm - jeżeli ''a > b'' to oblicz NWD(a-b, b) - jeżeli ''a < b'' to oblicz NWD(a, b-a) ==== Ćwiczenie: Potęga ==== Zaimplementuj funkcję o nazwie ''**potega**'', która dla danych dwóch liczb ''x'' i ''y'' wyznacza wartość potęgi ''xy''. Nie używaj w tym celu funkcji z biblioteki matematycznej. Zakładamy, że wykładnik potęgowania ''y'' jest liczba całkowitą. Funkcja realizuje następującą specyfikację:\\ **Argumenty funkcji**: liczba rzeczywista ''x'' oraz liczba całkowita ''y'' \\ **Wartość zwracana z funkcji**: wartość rzeczywista ''x'' podniesiona do potęgi ''y'' \\ Napisz program, który wczyta dwie liczby ''x'' i ''y'' a następnie korzystając z funkcji ''potega'' wyznaczy wynik ''xy'' oraz wypisze wynik na ekranie. * Spróbuj przystosować funkcję tak aby działała poprawnie również dla wartości ''y<0''. * Czy możliwa jest realizacja rekurencyjna funkcji ''potega'' ? **Przykład działania programu** Podaj dwie liczby: 2 2 2.000000 do potegi 2 wynosi 4.000000 Podaj dwie liczby: 2 10 2.000000 do potegi 10 wynosi 1024 Podaj dwie liczby: 2 -5 2.000000 do potegi -5 wynosi 0.031250 Podaj dwie liczby: 4.2 10 4.2 do potegi 10 wynosi 1708019.8 ==== Ćwiczenie: Ciąg Fibonacciego ==== Zaimplementuj funkcję o nazwie ''**fibonacci**'', która dla podanej liczby całkowitej ''n'' zwraca wartość ''n''-tego elementu ciągu Fibonacciego, który wyznaczamy ze wzoru: $$ F_{n}:=\left\{\begin{array}{ll} 0 & \text { dla } n=0 \\ 1 & \text { dla } n=1 \\ F_{n-1}+F_{n-2} & \text { dla } n>1 \end{array}\right. $$ Funkcja realizuje następującą specyfikację:\\ **Argumenty funkcji**: liczba całkowita ''n''\\ **Wartość zwracana z funkcji**: wartość elementu ''n'' ciągu Fibonacciego. W przypadku, gdy argument ''n<0'' to wynikiem jest 0. \\ Napisz program o nazwie ''fibonacci.c'', który wykorzysta funkcję ''fibonacci'' do wyznaczenia kolejnych elementów ciągu Fibonacciego. Dla wprowadzonej przez użytkownika liczby całkowitej ''k'' program wypisuje na ekranie wszystkie kolejne elementy ciągu Fibonacciego ''F0, F1, F2, ... , Fk''. **Przykład** k=10 0 1 1 2 3 5 8 13 21 34 55 ===== Zadanie: Liczby pierwsze ===== Zaimplementuj funkcję o nazwie ''**czy_pierwsza**'', która sprawdza, czy podana liczba jest liczbą pierwszą. \\ Funkcja powinna byc zaimplementowana zgodnie z poniższą specyfikacją: \\ **Argument funkcji:**: liczba całkowita ''n'' \\ **Wartość zwracana:** wartość całkowita ''1'' jeżeli argument ''n'' jest liczbą pierwszą. W przeciwnym wypadku funkcja zwraca wartość ''0''. Napisz program, który wykorzysta funkcję ''czy_pierwsza'' do wyznaczenia sekwencji wszystkich liczb pierwszych z zakresu od 1 do ''N'', gdzie wartość ''N'' podaje użytkownik na początku działania programu **Przykład** N = 10 Liczby pierwsze z zakresu od 1 do 10 1 2 3 5 7 Rozwiązanie (plik źródłowy) umieść w Moodle pod adresem https://moodle.umk.pl/WFAIIS/mod/assign/view.php?id=6254