Programowanie/Procedury i funkcje/Struktura jako jedyny ratunek

Z Wikibooks, biblioteki wolnych podręczników.
Przejdź do nawigacji Przejdź do wyszukiwania

Procedura jako jedyny ratunek. Dlaczego? Otóż, jeżeli znamy podstawowe konstrukcje algorytmiczne i potrafimy je zagnieżdżać, wówczas teoretycznie jesteśmy w stanie napisać każdy algorytm. Jednakże, długość kodu tegoż programu na ekranie komputera staje się coraz większa i po pewnym czasie nie jesteśmy w stanie objąć wszystkiego wzrokiem i umysłem. Z tego powodu powstało pojęcie podprogramu. Podprogram jest fragmentem kodu, który komunikuje się z programem głównym poprzez specjalne zmienne i może być wywoływany wielokrotnie przez program główny bądź inne podprogramy.

Zastosowanie podprogramu pozwala podzielić algorytm na zbiór mniejszych, wydzielonych części. Dzięki temu program jest czytelniejszy i łatwiejszy w utrzymaniu. Jeżeli podprogram jest wykorzystany wielokrotnie, wówczas program staje się mniejszy, bo jest mniej duplikacji kodu. Jedyną poważniejszą wadą podprogramów jest to, że ich uruchomienie wymaga czasem krótkotrwałego zatrzymania pracy programu w celu przełączenia się komputera na wykonanie nowego zadania. Opóźnienie jest zazwyczaj niezauważalne i staje się istotne przy intensywnym wykorzystaniu procesora. Jest to zagadnienie bardziej zaawansowane, poruszające kwestie wydajnościowe.

W językach imperatywnych podprogramy są zazwyczaj nazywane procedurami i funkcjami. Funkcja zwraca pewną wartość (i może komunikować się poprzez parametry), natomiast procedura nie zwraca wartości i komunikuje się z kodem nadrzędnym wyłącznie poprzez parametry.

Przykładową analogią jest sztuka teatralna. Scenariusz jest pisany dla postaci literackiej (np. Klara, Wacław), natomiast w rzeczywistej sztuce występują żywi aktorzy o innych imionach i nazwiskach (np. Danuta Szaflarska, Bogusław Linda). Podprogram jest pisany dla pewnych zmiennych zadeklarowanych w obrębie danego podprogramu. Część z nich (tak, jak owe postacie literackie) służy do komunikowania się z kodem nadrzędnym (który uruchomił ten podprogram). W przypadku sztuki teatralnej wystarczy wystawić sztukę, pamiętając o przekazaniu jej rzeczywistych aktorów dla określonych postaci literackich. W ten sposób algorytm sztuki teatralnej zostanie wykonany, a instrukcje zostaną wykonane przez rzeczywistych aktorów. Sztukę można też wystawić po raz kolejny z inną obsadą. I też będzie działało (algorytm jest ten sam, ale rezultat może być już inny, bo zmienili są aktorzy (konkretnie ich wartości)).

Podobnie w programie mamy początkowo zadeklarowanie zmienne o wybranych przez programistę nazwach. Ale załóżmy, że udało się nam wydzielić zadania, które będą wielokrotnie wykonywane. Przykładem jest rozwiązanie równania kwadratowego. Jest to zadanie dość uniwersalne. Może się przydać w przyszłości, także w innym programie. Zatem rzeczą przydatną jest wydzielenie takiego zadania w formie podprogramu. Matematyka uczy rozwiązywać takie równianie zapisanie w postaci:

a*x*x + b*x + c = 0

Natomiast w rzeczywistym programie problem może być opisany przez inne zmienne. Przykładem może być ruch jednostajnie zmienny w fizyce albo inny problem z zależnością kwadratową. Zamiast zmiennej x będziemy chcieli podstawić np. zmienną t określającą czas.

Do podstawiania zmiennych służy interfejs podprogramu. Jest on zawarty w nagłówku funkcji. Deklaruje on zmienne widoczne wewnątrz podprogramu, pod które będzie można podstawić zmienne (lub wartości zmiennych) z kodu nadrzędnego. Zmienne te są nazywane parametrami podprogramu.

Przykładowy interfejs podprogramu rozwiązującego równanie kwadratowe a*x*x + b*x + c = 0 można zapisać (w pseudokodzie) w postaci:

/*
// uwaga: współczynnik a musi być niezerowy, bo będzie dzielenie przez zero!

PROCEDURA OBLICZ_ROWNANIE_KWADRATOWE (RZECZYWISTA, WEJSCIOWA a;
                                      RZECZYWISTA, WEJSCIOWA b;
                                      RZECZYWISTA, WEJSCIOWA c; 

                                      BOOL, WYJSCIOWA jestRozwRzecz;
                                      RZECZYWISTA, WYJSCIOWA x1;
                                      RZECZYWISTA, WYJSCIOWA x2)
{
    // tutaj odbywa się obliczanie rozwiązania równania kwadratowego
    // widoczne są zmienne: a, b, c, jestRozwRzecz, x1, x2
    
    // można także deklarować inne zmienne, potrzebne do wykonania zadań, jednak nie będzie można
    // ich przekazać do kodu nadrzędnego.

    RZECZYWISTA Delta; // to jest zmienne lokalna (wyróżnik równania kwadratowego)

    // oblicz wyróżnik równania kwadratowego
    Delta = b*b - 4*a*c

    // podejmij decyzję o liczbie rozwiązań równania kwadratowego
    // przypadki trudne, potencjalne błędy i inne nieciekawe rzeczy można zrobić na początku
    // zostawiając na deser to, co prostsze
    IF(Delta < 0)
    {// Delta < 0, brak rozwiązań
    
        // Wyróżnik równania ujemny, brak rozwiązań rzeczywistych takiego równania
        // przekażemy informację o tym do programu głównego poprzez zmianę wartości parametru wyjściowego

        jestRozwRzecz = FALSE
        
    }// Delta < 0, brak rozwiążań
    ELSE
    {// Delta >= 0, jest 1 lub 2 rozwiązanie rzeczywiste


        // równanie posiada jedno rozwiązanie rzeczywiste
        jestRozwRzecz = TRUE

        // może być jedno lub dwa rozwiązania

        IF(Delta == 0)
        {// Delta == 0, jest jedno rozwiązanie rzeczywiste
            
            // możemy obliczyć pierwiastki rzeczywiste równania

            x1 = -b/(2*a)
            x2 = x1

        }// Delta == 0, jest jedno rozwiązanie rzeczywiste
        ELSE
        {// Delta > 0, są 2 rozwiązanie rzeczywiste

            // możemy obliczyć pierwiastki rzeczywiste równania
            // pamiętajmy o tym, że odjęcie numeryczne dwóch bliskich sobie wartości prowadzi
            // do dramatycznego spadku dokładności, w tym przypadku starałem się,
            // aby od  dodatniego -b nie odejmować dodatniego pierwiastka z delty.

            // drugi pierwiastek jest obliczany ze wzoru Viete'a, 
            // współczynnik a musi być niezerowy

            IF(b<0)
            {// b ujemne
              x1 = -b+sqrt(Delta)/(2*a)
              x2 = -b/a - x1
            }// b ujemne
            ELSE
            {// b nieujemne
                x1 = -b-sqrt(Delta)/(2*a)
                x2 = -b/a - x1
            }// b nieujemne
        }// Delta > 0, są 2 rozwiązanie rzeczywiste

    }// Delta >= 0, jest 1 lub 2 rozwiązanie rzeczywiste

}//PROCEDURA OBLICZ_ROWNANIE_KWADRATOWE
*/


W programie głównym można wywołać powyższy podprogram:

/*
PROGRAM DROGA
{
    // program oblicza czas, w którym ciało znajdowało się na zadanej wysokości.
    
    // deklaracje zmiennych programu
    RZECZYWISTA a, b, c, s1, s2
    BOOL SA_ROZW_RZECZ

    // deklaracja zmiennych wejściowych dla procedury
    a = 1
    b = 2
    c = 3

    // uruchomienie procedury
    WYKONAJ_PODPROGRAM OBLICZ_ROWNANIE_KWADRATOWE (a, b, c, SA_ROZW_RZECZ, s1, s2)
   
    // analiza wyniku
    IF(SA_ROZW_RZECZ == FALSE)
    {//brak rozwiązań

        WRITE ("ciało nie osiągnęło danej wysokości")

    }//brak rozwiązań
    ELSE
    {//są rozwiązania

        IF(x1 == x2)
        {//jedno rozwiązanie

            WRITE("Ciało osiągnęło punkt szczytowy w czasie: ", x1)

        }//jedno rozwiązanie
        ELSE
        {//dwa rozwiązania

            WRITE("Ciało osiągnęło zadaną wysokość w czasie: ", x1, " oraz ", x2)
        }//dwa roziwązania

    }//są rozwiązania

}// PROGRAM DROGA
*/

W programie zadeklarowano zmienne, które zostały następnie przypisane podczas wywołania procedury. W procedurze zadeklarowano zmienną x1 i x2. Podczas wykonania procedury została jej podstawiona zmienna s1 i s2. Po wyjściu z procedury wartości wyjściowe (m.in. s1 i s2 ) mają wartości podstawione w ciele procedury. Oznacza to, że po wywołanie procedury nastąpiło zwrócenie wyników obliczeń. Jest to nazywane efektem ubocznym wywołania podprogramu.

Uwaga: w języku C do opisanego zwrócenia wartości z wnętrze podprogramu (poprzez parametry) konieczne jest użycie wskaźników.