Jason. Cała informatyka w jednym miejscu!

Dotarło do mnie, że pominąłem jeden z najbardziej fundamentalnych wątków programowania jakie każda szanująca się strona internetowa dotycząca informatyki powinna mieć. Po prostu, łał! Wciskamy gaz do dechy i tłumaczę na szybkości do czego służy funkcja w programowaniu, a także jaka jest jej definicja, bo co to za programista który pisze, nie wie co pisze i nie potrafi krótko zdefiniować tego, co pisze ;). Zapraszam!

JAKĄ FUNKCJĘ PEŁNI FUNKCJA?

Gra słów na początek. Kochani Czytelnicy, jeśli jeszcze nie macie pojęcia co to jest funkcja (ang. "function"), a napisaliście ich już wiele, to zróbcie sobie tę przyjemność i przeczytajcie ten artykuł bardzo, bardzo, bardzo uważnie. W moim życiu upłynęło kilka lat od uczenia się pisania kodu, zanim poznałem definicję. Wstyd? Nieee, nic z tych rzeczy. Dlatego chcę oszczędzić Wam narażania się na krótkie, ale rozbrajające pytanie "co to jest funkcja?", żebyście potem nie szukali nerwowo definicji w Google:

Funkcją nazywamy podprogram zawierający własny blok instrukcji, który może dysponować własną listą parametrów formalnych, jak również zwracać jakąś wartość.

Pokolorowałem słowo, które uważam za kluczowe: podprogram. To jest wydzielona część kodu opatrzona nagłówkiem / nazwą / etykietą (nazwij to jak sobie życzysz), której można przypisać parametry nazywane też "parametrami formalnymi". Są to zmienne lokalne występujące tylko w obrębie tejże funkcji.

DO CZEGO SŁUŻY FUNKCJA?

Funkcję zobaczysz w każdym programie napisanym w języku programowania, który jej wymaga jako punktu startowego. Na przykład język C wymaga zawsze funkcji "main", od której rozpoczyna się wykonywanie programu. Są dwa istotne powody, które nadają funkcji kolosalnego znaczenia:

  1. dzieli kod na mniejsze części,
  2. pozwala wykonać te same instrukcje wielokrotnie.

Wtrącę dodatkowy komentarz do punktu pierwszego. Najlepiej w taki sposób definiować funkcje, żeby każda z nich wykonywała jedno zadanie. To może być otwarcie pliku, zmiana koloru tekstu i tym podobne twory. Nie radzę mieszać ze sobą wielu zadań naraz, bo wtedy funkcja posiada kilkadziesiąt linijek kodu.

Co do punktu #2, to funkcja zapobiega powstawaniu nadmiarowości tego samego kodu w wyniku jej wywoływania (o tym za moment). Nadaje to szczególnego znaczenia kiedy opakowujemy te wywołania w pętle.

DEFINICJA FUNKCJI W KODZIE ŹRÓDŁOWYM

Definiowanie treści funkcji zależy od języka programowania jakiego używamy i naprawdę, żeby zmieścić wszystkie dostępne postacie, wyszłoby chyba z trzydzieści różnych wzorów. Pokażę jedynie na przykładzie języka C, gdyż artykuł dotyczy programowania ogólnie, a C traktuję jako "pioniera" języków wysokiego poziomu. Ale miejcie na względzie, że nie wszystkie języki będą kierować się tą pierwotną strukturą. Tak wygląda najprostsza postać:

void doSomething()
{
	// treść
}

Najpierw określamy typ zwracanej wartości przez funkcję. O tym będzie za chwilę. Następnie nadajemy jej nazwę (etykietę) którą będziemy się posługiwać podczas wywoływania, o czym także będzie już za momencik. Para nawiasów okrągłych to miejsce dla zdefiniowania ewentualnych parametrów formalnych jakie może przyjmować funkcja do wykonywania swoich zadań na wskazanych danych.

Po podaniu tych informacji, wstawiamy parę nawiasów klamrowych. To określa blok treści funkcji nazywany też lokalnym zasięgiem (ang. "local scope"). W środku niego wtłaczamy wszystkie instrukcje jakie mają się wykonać po wywołaniu funkcji. I to jest wszystko z najważniejszych wiadomości wytłumaczonych powierzchownie.

Warto rozróżniać czym jest definicja, a deklaracja funkcji. Deklaracja występuje wtedy, kiedy wstawicie sam nagłówek funkcji i zakończycie średnikiem. W zależności od języka, będzie to nazywane prototypem funkcji (C/C++), bądź metodą abstrakcyjną (Java/C#):

void doSomething();

Jeżeli określacie dodatkowo co ma zostać wykonane w środku klamerek, to to już jest definicja! Przejdziemy teraz do szczegółów.

ZWROT WARTOŚCI PRZEZ FUNKCJĘ

Powróćmy do samego początku. "void" oznacza dosłownie "pustkę". W kontekście omawianego tematu to znak, że funkcja nie zwraca żadnej wartości. Wtedy NIE WOLNO wstawić w środku niej instrukcji "return" z wartością jakiegokolwiek typu, która to odpowiada za jej zwrot na wyjściu. Możecie użyć samego "return" bez dodatków:

void doSomething()
{
	return;
}

To będzie oznaczało natychmiastowe przerwanie wykonywania dalszych instrukcji. Co jeżeli nasza funkcja ma zwracać jakąś wartość? Jak wyżej - to zależy w dużej mierze od języka. W większości przypadków (w tym w C), wystarczy podmienić słówko "void" na typ danych jaki chcemy zwracać:

int doSomething()
{
	return 25;
}

Już ;)! W takiej sytuacji MUSICIE wstawić instrukcję zwracającą wartość, bo inaczej dojdzie do nieporozumienia! Jeśli wstawicie instrukcję warunkową kontrolującą przepływ programu (innymi słowy, program może pójść jedną z wielu ścieżek), to wówczas na końcu KAŻDEJ ścieżki ma się znaleźć "return"!!!

int doSomething()
{
	if(1)
	{
		return 25;
	}
	else
	{
		return 10;
	}
}

FUNKCJA Z PARAMETRAMI!

Zdefiniujemy sobie teraz jakieś parametry! Parametr funkcji to nic innego jak zmienna o zasięgu lokalnym, która występuje tylko w obrębie bloku treści funkcji. Określa się to bardzo prosto. Wystarczy zdefiniować sobie parametr w środku pary nawiasów okrągłych, w taki sam sposób jak definiuje się zmienne "normalnie":

void doSomething(int i)
{
	// treść
}

Widać? Dodałem liczbę całkowitą o nazwie "i". Funkcja będzie mogła się nią posłużyć w trakcie wykonywania swoich instrukcji. Na przykład:

void doSomething(int i)
{
	printf("%d", i);
}

Natomiast na zewnątrz nie będzie w ogóle istnieć! Dlatego jest mowa o zasięgu lokalnym. Zasięg globalny ma zmienna, która została zdefiniowana na zewnątrz funkcji.

Potrzebujecie więcej niż jeden parametr? Zgoda! Wystarczy oddzielić je przecinkiem:

void doSomething(int i, float f)
{
	// treść
}

WYWOŁYWANIE FUNKCJI

Została ostatnia sprawa, czyli wywoływanie funkcji. To jest uruchomienie wykonywania wszystkich czynności zawartych w jej bloku. Gdy w innej funkcji wstawicie coś takiego:

doSomething();

to będzie oznaczało, że wywołaliście funkcję o nazwie "doSomething". Program wtedy "wchodzi" do jej wnętrza, wykonuje wszystkie instrukcje które zostały tam określone, dochodzi do klamerki zamykającej i wraca z powrotem w miejsce wywołania. Tak to działa w języku abstrakcyjnym. Taki zapis stosujecie, kiedy funkcja nie zwraca żadnej wartości, czyli jest typu "void".

Kiedy przyjmuje jakieś parametry, musimy je ściśle określić w wywołaniu. Wstawiamy ich zawsze tyle, ile zostało zdefiniowanych w nagłówku. Na przykład:

doSomething(5);
doSomething(25, 5.6f);

To będą poprawne wywołania funkcji przyjmujących kolejno jeden i dwa parametry (zgodnie z powyższymi zapisami). Określane są powszechnie "argumentami", choć ja wolę pojęcie "parametr aktualny". Pamiętajcie też o tym, że typy danych i kolejność wartości muszą się ze sobą zgadzać! Znowu, to zależy od języka jak bardzo jest rygorystyczny i jak "mocną" posiada tak zwaną "typizację". Język C jest językiem o słabej typizacji, więc w gruncie rzeczy będzie mu "zwisać" co tam wstawimy, co będzie miało później konsekwencje w postaci niezdefiniowanego zachowania programu czy "obcięcia" części ułamkowej. Język Java na ten przykład już Wam nie odpuści i każe wstawić w miejsce każdego parametru typ zgodny z definicją funkcji.

Został jeszcze jeden przypadek, kiedy funkcja zwraca wartość. Wtedy wywołanie musi zostać trochę zmodyfikowane. Mianowicie, musi przyjmować postać przypisywania wartości. Na przykład:

int i = doSomething();

Zgodność typów ze słówkiem podanym jako pierwszym w definicji, obowiązkowa! Powtórzę jeszcze raz, mocniej czy słabiej w zależności od języka. Nic nie stoi na przeszkodzie, aby zwracaną wartość potraktować jak jeden z operandów:

int i = 25*doSomething();

Efekt? Pomnożenie otrzymanej wartości na wyjściu funkcji "doSomething" przez dwadzieścia pięć. Oczywiście wybijcie sobie z głowy takie rzeczy, jeśli typ nie jest liczbowy ;).

FUNKCJA VS. PROCEDURA VS. METODA

Dodatkowa lekcja o terminologii. Są takie języki np. Pascal, które rozróżniają funkcje zwracające wartość od funkcji niezwracających wartości. Warto wiedzieć, że jeśli jest kładziony nacisk na odróżnianie jednych od drugich, to funkcję typu "void" nazywamy procedurą. Funkcja dalej pozostaje funkcją, jeżeli zwraca jakąkolwiek wartość, jednak przestaje nią być kiedy korzystamy z paradygmatu obiektowego np. język Java. Kiedy funkcja należy do jakiejś klasy, wtedy "przemienia się" w metodę (niezależnie od tego, czy coś zwraca czy nie). W języku C nie ma osobnego odróżniania funkcji od procedury i tam wszystko jest funkcją.


Zdaje mi się, że nie zapomniałem o niczym istotnym na temat funkcji jako takiej w programowaniu. Wszystkim, którzy przeczytali i dotrwali do końca, dzięki!

PODOBNE ARTYKUŁY