Sierpień 2, 2018

Funkcje

Funkcje

W tej lekcji kursu zapoznamy się z funkcjami – choć jedną już znasz – main. Będzie to ostatnia lekcja pierwszego tygodnia, po przerobieniu tygodnia pierwszego powinieneś mieć wystarczającą wiedzę do rozpoczęcia kolejnego etapu – czyli obiektowości.

Czym są te funkcje…

Funkcje są niczym innym jak blokami instrukcjami, które są wykonywane po wywołaniu funkcji – czyli, gdy ją zawołamy do działania.

Funkcje można utożsamiać z funkcjami matematycznymi – mogą one przyjmować argument – przysłowiowy x w funkcjach matematycznych i zwracać jakąś wartość. Tak w skrócie mogą wyglądać niektóre funkcje.

Przedstawmy sobie schemat funkcji:

[widoczność] [dodatkowe-opcje np. static] [typ-zwracany] [nazwa-funkcji] ([opcjonalnie: argument]) {
  // ciało funkcji
}

Może wygląda na pierwszy rzut oka trochę skomplikowanie, ale wszystko się wyjaśni podczas pisania kodu.

Widoczność definiuje to gdzie nasza funkcja jest widoczna – aktualnie nasze funkcje będą publiczne, o widoczności dowiesz się podczas drugiego tygodnia kursu.

Funkcja może mieć jeszcze kilka dodatkowych opcji, jedną z opcji, z której korzystamy jest static – mówi ona o tym, że funkcja (lub zmienna) jest statyczna, czyli dostęp do niej jest możliwy bez tworzenia obiektu. Jest to wszystko na razie tajemnicze dla Ciebie, jednak wszystko się rozwiąże podczas omawiania obiektowości. Na ten czas wszystkie nasze funkcje muszą być statyczne, aby móc je wywoływać w metodzie main.

Typ zwracany to nic innego jak typ danych – może być prymitywny np. int, double, float – może być to obiekt np. String, albo może nic nie zwracać – jest to tak zwany void.

Nazwa funkcji jest dowolna, najlepiej jednak trzymać się konwencji, aby metoda mówiła za co jej odpowiedzialna np. calculateAverage, findMax, findMin. 

W nawiasie możemy podać argumenty – lecz nie musimy – dzięki argumentom, do funkcji możemy przekazać zmienne, tablice lub obiekty i na nich wykonywać jakieś operacje w ciele funkcji.

W ciele funkcji musi się jeszcze pojawić słowo return – które zwraca stan z funkcji – nie musi ono występować tylko w przypadku funkcji typu void – choć może. Zastosowanie tego słowa wyjaśni się w trakcie omawiania kilku funkcji.

Szablon startowy

Klasycznie zdefiniujemy sobie szablon, od którego razem zaczniemy.

public class Main {
    public static void main(String[] args) {
        int [] values = {5, 0, 6, 12, 44, 101, 42, 12, 16, 120};
    }
}

Z tego miejsca wyruszymy w poznawanie funkcji.

Funkcje

Tak jak wspominałem funkcję mogą zwracać różny typ oraz przyjmować argumenty – przerobimy sobie kilka typów.

void

Specyficznym typem funkcji jest void, który tak naprawdę nie zwraca żadnej wartości z funkcji – dlatego tego typu nie spotkałeś podczas lekcji z typami danych.

Zadaniem pierwszej metody, będzie wypisanie na ekran napisu: „Hello!”.

public class Main {

    public static void sayHello() {
        System.out.println("Hello!");
    }

    public static void main(String[] args) {
        int [] values = {5, 0, 6, 12, 44, 101, 42, 12, 16, 120};
    }
}

Teraz naszą funkcję możemy wywołać w funkcji main.

public class Main {

    public static void sayHello() {
        System.out.println("Hello!");
    }

    public static void main(String[] args) {
        int [] values = {5, 0, 6, 12, 44, 101, 42, 12, 16, 120};
        sayHello();
    }
}

Wywołanie funkcji odpywa się po prostu przez jej nazwę wraz z nawiasami. Po uruchomieniu  programu otrzymamy:

Hello!

Dodajmy do naszej funkcji argument, podajmy imię i wykorzystajmy je podczas przywitania.

public static void sayHello(String name) {
    System.out.println("Hello " + name);
}

Tym razem w wywołaniu funkcji musimy zadbać o zapewnienie argumentu inaczej kod się nie skompiluje.

sayHello("Kamil");

Po wywołaniu otrzymamy:

Hello Kamil

Czyli nasza funkcja staję się coraz bardziej uniwersalna, możemy przywitać każdego nie zmieniając ciała funkcji.

Zastosujmy instrukcję return w funkcji void, zabezpieczmy się, gdy ktoś poda pustego Stringa.

public static void sayHello(String name) {
    if(name.length() == 0){
        return;
    }
    System.out.println("Hello " + name);
}

Jeżeli ktoś poda pustego stringa (czyli jego długość będzie równa 0) to zostanie wykonana instrukcja return – czyli automatycznie wyjdziemy z ciała funkcji. Przetestujmy działanie przy takim wywołaniu:

sayHello("");

A w konsoli nic nie otrzymaliśmy – czyli return automatycznie przerywa wykonywanie ciała funkcji.

int

Skoro potrafimy już pisać proste funkcję to napiszmy funkcję do obliczania średniej arytmetycznej – wykorzystamy oczywiście do tego pętle – najlepiej foreach. Tworzymy ciało funkcji:

public static float calculateAverage(int [] numbers) {
    
}

Przeanalizujmy sygnaturę funkcji: mamy typ zwracany float, ponieważ średnia nie musi być liczbą całkowitą, nazwa funkcji mówi wyraźnie za co jest odpowiedzialna, a jako argument przekazaliśmy tablicę liczb, dla których obliczymy średnią.

public static float calculateAverage(int [] numbers) {
    int sum = 0;
    for(int number : numbers){
        sum += number;
    }

}

Nic skomplikowanego – przechodzimy przez wszystkie elementy podanej tablicy i sumujemy je, aby później wyznaczyć średnią.

Czas na zwrócenie średniej, czyli zwracamy sumę podzieloną przez liczbę elementów tablicy.

return sum / (float) numbers.length;

Pamiętajmy o zrzutowaniu mianownika na float, aby kompilator nie uciął nam wartości po przecinku!

Dzięki metodzie return zwracamy wynik dzielenia sumy przez ilość elementów – czyli średnią. Można oczywiście to rozbić na dwa etapy:

float average = sum / (float) numbers.length;
return average;

Wybór należy już do Ciebie.

Ostatecznie nasza funkcja wygląda tak:

public static float calculateAverage(int [] numbers) {
    int sum = 0;
    for(int number : numbers){
        sum += number;
    }
    float average = sum / (float) numbers.length;
    return average;
}

Wywołajmy ją z main:

float average = calculateAverage(values);

Jak widzisz funkcję przypisuję do zmiennej float – bo tak naprawdę ta funkcja zwraca nam wartość float, która automatycznie przypisuję do zmiennej average.

Zostało nam tylko wyświetlić wartość średniej:

System.out.println("Average: "+average);

I po uruchomieniu otrzymujemy:

Average: 35.8

Czyli wszystko działa poprawnie. 😉

boolean

Nie chcę omawiać funkcji ze wszystkimi możliwymi typami liczbowymi, za to możemy spróbować zbudować funkcję, która będzie zwracać tylko true lub false.

Funkcja będzie miała za zadanie sprawdzić na podstawie imienia czy podana osoba jest mężczyzną czy kobietą.

Sygnatura takiej funkcji będzie wyglądać tak:

public static boolean isMale(String name) {
    
}

Spójrz, że nazwa funkcji, która zwraca boolean często ma przedrostek is.

W języku polskim na podstawie ostatniej litery możemy prawie w 100% stwierdzić płeć osoby – w takim razie do dzieła.

int lastCharIndex = name.length() - 1;
boolean isFemale = name.charAt(lastCharIndex) == 'a';

Pierwsza linia wylicza index ostatniej litery – String jest indeksowany tak samo jak tablica – czyli do 0 do n-1.

Druga linia odpowiada za to, czy ostatnia litera jest równa ‚a’. Metoda charAt zwraca znak, pod wyznaczonym indeksem – my chcemy ostatni znak.

Na podstawie tego możemy zwrócić fałsz – ponieważ funkcja sprawdza czy osoba jest mężczyzną:

if(isFemale){
    return false;
}

Ale co jeśli nie wykona się ten return? To trzeba dodać kolejny na samym końcu metody, który zwróci prawdę.

public static boolean isMale(String name) {
    int lastCharIndex = name.length() - 1;
    boolean isFemale = name.charAt(lastCharIndex) == 'a';

    if(isFemale){
        return false;
    }

    return true;
}

Jeżeli return false nie przerwie działania programu to funkcja zwróci true.

Trzeba pamiętać, że funkcja, która jest inna niż void musi zawsze zwracać jakąś wartość!

Wywołajmy sobie, więc funkcje w main:

boolean isMale = isMale("Kamil");
System.out.println("Is male: "+ isMale);

I po wywołaniu otrzymujemy:

Is male: true

Podsumowanie

Po tej lekcji powinieneś już widzieć, że funkcje są stworzone po to, aby wydzielać w osobny powtarzalny kod i wywoływać go wielokrotnie przy użyciu tylko nazwy funkcji – pozwala to na tworzenie dużo czytelniejszego kodu.

Trzeba też pamiętać, że funkcja może przyjmować wiele argumentów, oddzielonych od siebie przecinkami.

Aby nie mieć problemu w przyszłości z tworzeniem funkcji zachęcam Cię do napisania i przetestowania kilku funkcji.

  1. Stwórz funkcję, która będzie miała za zadanie wyświetlić napis: „Hello, I am {name} {lastname}”. Pamiętaj, że name lastname powinny być osobnymi argumentami. Zastanów się lepiej zastosować funkcję void i wyświetlić automatycznie napis, czy może zwrócić zbudowanego Stringa i dopiero wyświetlić go w funkcji main?
  2. Stwórz funkcję, która w tablicy podanej jako argument znajdzie element najmniejszy i go zwróci.
  3. Stwórz funkcję, która w tablicy podanej jako argument znajdzie element największy i go zwróci.
  4. Stwórz funkcję, która wyświetli wszystkie elementy tablicy podanej jako argument funkcji.
  5. Stwórz funkcję, która obliczy sumę wszystkich elementów tablicy podanej jako argument.
  6. Stwórz funkcję, która obliczy średnią wszystkich elementów tablicy podanej jako argument.

Pamiętaj o nadawaniu nazw funkcji tak, aby było wiadomo jakie jest jej zadanie!

Rozwiązania wszystkich zadań możesz znaleźć tutaj.