Sierpień 2, 2018

Pętle

Pętle

Jeżeli wykonałeś zadania z poprzedniej lekcji kursu o tablicach z pewnością musiałeś sporo się napisać – w dzisiejszej lekcji pokażę Ci jak można skrócić taki kod przy użyciu pętli – lub przez niektórych nazywanych – cykli.

Czym są pętle

Pętle są niczym innym jak instrukcją, która pozwala wykonać jakąś akcję ustaloną ilość razy – zależnie od tego jaki warunek końcowy zostanie zdefiniowany.

Po prostu mają za zadanie nam ułatwić życie – aby nie pisać 100 razy podobnego wywołania akcji – nasz kod aplikacji strasznie by się wydłużył!

Dość teorii, przejdźmy do praktyki.

Start

Do rozpoczęcia nauki pętli przyda się z pewnością szablon z dwoma tablicami, na których będziemy działać.

Po poprzedniej lekcji powinieneś potrafić bez problemu stworzyć tablicę jednowymiarową:

int [] values = {1, 65, 5, 10, 12, 58, 23, 24, 20, 12};

Oraz stworzyć tablicę dwuwymiarową:

int [][] positions = new int[3][3];

Czyli wystartujemy z takim szablonem:

package pl.maniaq;

public class Main {
    public static void main(String[] args) {
        int [] values = {1, 65, 5, 10, 12, 58, 23, 24, 20, 12};
        int [][] positions = new int[5][3];
    }
}

for

Zaczniemy od pętli od for – wszystkie pętle od siebie głównie różnią się składnią, dlatego od razu przedstawię Ci jak wygląda składnia pętli for.

for (warunek-startowy-licznika; warunek-pętli; zmiana-licznika) {
}

Na wstępie warto powiedzieć, że naszym licznikiem jest po prostu zmienna całkowita. Ma ona swój warunek startowy – w przypadku tablic będzie to wartość 0 – ponieważ od 0 indeksujemy tablicę.

Warunkiem pętli jest ustalona wartość licznika – czyli wykonuj pętle, aż do momentu osiągnięcia takiej wartości. W przypadku tablic będzie to oczywiście n-1.

Zmiana licznika określa tak naprawdę co ma się stać po zakończeniu jednego kroku – zazwyczaj jest to po prostu zmniejszenie lub zwiększenie licznika, aby móc zbliżyć się do warunku końcowego.

Na podstawie tej teorii stwórzmy sobie pętle przechodzącą po wszystkich elementach tablicy values.

for(int i=0;i<values.length;i++){
    
}

Tak jak wspominałem, w przypadku tablic zaczynamy liczyć od 0 – dlatego i = 0.

Pętla wykonuje się dopóki warunek pętli jest spełniony – jeżeli nie to pętla przerywa swoje działanie. Mając tablicę, możemy zobaczyć ile elementów ma tablica przy użyciu length. Nasz licznik (czyli i) musi zawsze być mniejszy od liczby elementów – jeżeli osiągnałby wartość równą length to wyszlibyśmy poza zakres tablicy – pamiętaj o indeksowaniu tablic.

Ostatnim blokiem jest zwiększenie licznika o jeden, aby przejść po każdym elemencie tablicy.

Skoro mamy już stworzoną pętle, która przechodzi po wszystkich elementach to czas na wykonywanie jakiś akcji. Wyświetlmy wszystkie elementy tablicy na ekran konsoli.

for(int i=0;i<values.length;i++){
    System.out.println(values[i]);
}

I w taki o to sposób wyświetliliśmy 10 elementów tablicy przy użyciu 3 linijek kodu – czy może wolisz poniższe rozwiązanie?

System.out.println(values[0]);
System.out.println(values[1]);
System.out.println(values[2]);
System.out.println(values[3]);
...
System.out.println(values[9]);

O ile przy 10 elementach jest to możliwe, to co w przypadku, gdy mamy ich 1000 lub milion?

Potrafiąc wyświetlić już elementy tablicy – może warto by było wszystkie je zsumować i wyświetlić wynik na ekran? Spróbujmy!

Na początku będziemy potrzebowali zmiennej, do której będziemy zapisywać sumę.

int sum = 0;

Następnie przechodzimy po wszystkich elementach dodając je do zmiennej sum.

for(int i=0;i<values.length;i++){
    sum = sum + values[i];
}
System.out.println("Suma elementów to: "+sum);

Zapis sum = sum + values[i] oznacza, że do zmiennej sum dodajemy wartość – ten zapis można zastąpić również zapisem: sum += values[i];

Po uruchomieniu programu otrzymujemy:

Suma elementów to: 230

I w taki o to sposób otrzymaliśmy sumę wszystkich elementów – kilka linii kodu, a może zsumować nieskończoną ilość liczb – to jest właśnie zaleta używania pętli.

Przejdźmy do kolejnych pętli.

while

While jest pętlą, której podajemy warunek – dopóki ten warunek jest spełniony to pętla się wykonuje.

Dzięki pętli while można bez problemu stworzyć pętle nieskończoną – taką która będzie się wykonywała do końca działania programu lub zatrzymania jej przy użyciu instrukcji break.

Na początku zadeklarujmy sobię pętle nieskończoną:

while(true){
    System.out.println("Test");
}

Po uruchomieniu programu konsola będzie się spamować napisem Test, dopóki nie zamkniemy jej. Zróbmy coś bardziej ciekawego.

Poprośmy użytkownika o napisanie 0 jeżeli chce zakończyć działanie programu.

Scanner scanner = new Scanner(System.in);
int status = 1;
while(true){

}

Co teraz będzie warunkiem w pętli while? Skoro pętla while wykonuje się dopóki warunek jest prawdą to musimy go dopasować odpowiednio do naszej liczby. Czyli pętla musi się wykonywać dopóki status jest różny od 0.

while(status != 0) {
 
}

Dzięki takiemu warunkowi pętla się będzie wykonywać dopóki użytkownik nie wpisze liczby 0.

Dodajmy kilka informacji dla użytkownika i odczytajmy wartość z klawiatury.

Scanner scanner = new Scanner(System.in);
int status = 1;
while(status != 0){
    System.out.println("Napisz 0 jeżeli chcesz zakończyć działanie programu");
    status = scanner.nextInt();
}

Przetestujmy nasz program.

Napisz 0 jeżeli chcesz zakończyć działanie programu
3
Napisz 0 jeżeli chcesz zakończyć działanie programu
0

Process finished with exit code 0

Jak widać po wpisaniu 0 program się zakończyl – co wiąże się z tym, że pętla też musiała. 😉

do while

Dużo rzadziej używana pętla od innych – jednak warto o niej wspomnieć. Jest ona rozszerzeniem pętli while – z jedyną małą różnicą.

Pętla do while wykonuje się zawsze co najmniej raz. Polega to na tym, że najpierw wykonuje się blok pętli, a dopiero na koniec jest sprawdzany warunek!

Przejdźmy do zbudowania takiej pętli:

do {
    
} while(true);

Tak wygląda pętla nieskończona przy użyciu do while – zróbmy inny myk – zamieńmy true na false – pętla whiel nie wykonałaby się ani razu – co się stanie z do while?

do {
    System.out.println("Done.");
} while(false);

W konsoli otrzymujemy:

Done.

Oznacza to, że pomimo warunek nie jest spełniony to i tak na początku pętla do while wykonuje najpierw blok kodu!

foreach

Na koniec zostawiłem swoją ulubioną pętle – czyli foreach – choć w Javie nie ma akurat takiego słowa kluczowe, tylko tworzy się ją również przy użyciu słowa for. Tylko jej struktura jest nieco inna od zwykłej pętli for.

for (typ-kolekcji nazwa-iteratora : tablica) {
}

Typ kolekcji musi być równy z typem jaki przechowujemy w naszej tablicy.

Nazwa iteratora jest to samo co nasze w pętli for.

Tablica to po prostu tablica, po której będziemy iterować.

Implementacja w naszym kodzie pętli foreach będzie wyglądać tak:

for(int value : values){
     
}

Jak widzisz nie musimy już dbać o żadne rozmiary tablic i zakresy – dlatego foreach jest najbardziej czytelną i super pętlą.

Wyświetlmy po prostu wszystkie elementy naszej tablicy:

for(int value : values){
    System.out.println(value);
}

Jak widać rozwiązanie jest dużo czytelniejsze – nie musimy już pisać żadnych nawiasów kwadratowych, ani martwić się o żadne warunki końcowe – co prawda są sytuacje, gdy musimy skorzystać ze zwykłej pętli for np. gdy chcemy przejść co drugi element tablicy.

break

W poprzednich pętlach nic powiedziałem o instrukcji break – już Ci pokazuję o co w niej chodzi.

Instrukcja break pozwala na przerwanie działania najbliższej pętli – po prostu z niej wychodzimy jak gdyby nigdy nic. Już pokazuję przykład – przechodźmy po elementach tablicy i przerwijmy, gdy napotkamy liczbę parzystą.

Klasycznie potrzebujemy pętli, aby przejść po wszystkich elementach:

for(int i=0;i<values.length;i++){

}

Potrzebny jest nam jakiś warunek sprawdzający czyli liczba jest parzysta – powinien już Ci się kojarzyć operator modulo.

boolean isEven = values[i] % 2 == 0;

Dzięki temu warunkowi sprawdzamy, czy wartość z tablicy pod indeksem i jest parzysta. Dodajmy do tego if – gdzie będziemy mogli użyć instrukcji break.

for(int i=0;i<values.length;i++){
    boolean isEven = values[i] % 2 == 0;
    if(isEven){
        System.out.println("Na indeksie: " + i + " napotkałem liczbę parzystą.");
        break;
    }
}

Po uruchomieniu programu otrzymujemy taki rezultat:

Na indeksie: 3 napotkałem liczbę parzystą.

Index równy 3 – czyli trafiło na liczbę 10 – która tak naprawdę jest na miejscu 4. Jednak ponownie przypominam Ci o indeksowaniu tablic. 😉

Pętla w pętli

Obiecałem wrócić do tablic dwuwymiarowych podczas omawiania pętli – i nadszedł czas w akapicie o występowaniu pętli w pętli. Może na początku dosyć skomplikowane – ale wszystko zaraz okaże się jasne.

Mamy naszą tablicę positions, która jest dwuwymiarowa, stwórzmy pierwszą pętlę, która będzie iterowała po pierwszym indeksie tablicy.

for(int i=0;i<positions.length;i++){
    
}

Dzięki niej przejdziemy wszystkie tablicy, potrzebujemy jeszcze pętli, która przejdzie po wszystkich elementach każdej tablicy – dlatego musimy zadeklarować pętlę wewnętrzną.

for(int i=0;i<positions.length;i++){
    for(int j=0;j<positions[i].length;j++){
        
    }
}

Pętla wewnętrzna będzie wykonywana dla każdej iteracji pętli zewnętrznej – dzięki czemu przejdziemy po wszystkich elementach.

Pozwólmy jeszcze użytkownikowi na wpisanie wartości we wszystkie pola.

Scanner scanner = new Scanner(System.in);
for(int i=0;i<positions.length;i++){
    for(int j=0;j<positions[i].length;j++){
        System.out.println("Podaj liczbe dla komórki: ["+i+"]["+j+"]");
        positions[i][j] = scanner.nextInt();
    }
}

Zauważ, że pętla wewnętrzna jest odpowiedzialna za iterowanie po elementach w tablicach, zaś pętla zewnętrzna (i) jest odpowiedzialna za iterowanie po tablicach.

Uruchom program i wpisz wszystkie wartości i upewnić się, że rozumiesz indeksację i iterowanie po tablicach dwuwymiarowych.

Podsumowanie

Na koniec przygotowałem dla Ciebie krótkie zadania, które pozwolą Ci przyswoić wiedzę z pętli.

  1. Oblicz sumę liczb: 12, 532, 43, 231, 6, 534, 329, 23 przy użyciu tablicy oraz pętli.
  2. Oblicz ile jest liczb w parzystych w zbiorze liczb: 12, 67, 58, 49, 44, 21, 23, 22, 44, 59, 40. Użyj tablicy oraz pętli.
  3. Oblicz wynik mnożenia wszystkich liczb: 12, 2, 15, 9, 11, 8, 15. Użyj pętli oraz tablicy.
  4. Mając tablicę 32, 21, 0, 1, 23, 12, 42, 55, 13, 5  znajdź jej największą i najmniejszą wartość i wypisz je na ekran konsoli.
  5. Stwórz tablicę dwuwymiarową o rozmiarze 3×3 pozwól użytkownikowi na wypełnienie jej każdej komórki. Po wypełnieniu całej tablicy wypisz wszystkie elementy tablicy na ekran – wiersz pod wierszem.
  6. Stwórz tablicę dwuwymiarową o rozmiarze 3×3 pozwól użytkownikowi na wypełnienie jej każdej komórki. Po wypełnieniu całej tablicy wylicz sumę wierszy – bez różnicy czy pierwszy index weźmiesz za kolumnę czy wiersz – chodzi o to, aby wynikiem działania programu były trzy sumy wierszy.
  7. Przy użyciu pętli nieskończonej stwórz menu dla kalkulatora, w którym  można wybrać opcję dodawania/odejmowania/mnożenia/dzielenia – po użyciu wybranej funkcji menu powinno na nowo się wyświetlić, a użytkownik powinien nadal móc używać kalkulatora.

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

W następnej lekcji pokażę Ci w jakim celu są tworzone funkcję i jak je tworzyc.