2. Warunkowe pętle iteracyjne: instrukcje while i do..while
Instrukcja while ma następującą postać:
while (wyr) ins gdzie:
Działanie pętli while jest następujące. Wyrażenie wyr reprezentuje podobny
"warunek" jak w instrukcji if : jego wartość jest wyliczana i jeśli jest
równa true to wykonywana jest instrukcja ins, w przeciwnym razie sterowanie
przekazywane jest do pierwszej instrukcji po pętli while. Przypomnienie: znaki można traktować jak liczby, bo są w komputerze reprezentowane
za pomocą kodów liczbowych
Na przykład poniższy fragment wyprowadza wszystkie małe litery alfabetu angielskiego (będzie on działał prawidłowo dla wszystkich tablic kodowych, w których małe litery alfabetu angielskiego zajmują ciągłą przestrzeń tzn. po a następuje b, po b c - itd; "następowanie po" oznacza, że kod następnego znaku jest o 1 większy od kodu znaku poprzedniego). char c = 'a'; while (c <= 'z') System.out.print(c++);
Proszę sprawdzić działanie tego fragmentu we własnym programie. int n = 0; char c = 'z'; while(c >= 'a' && n < m) { System.out.println(c--); n++; }
Proszę sprawdzić działanie tego fragmentu we własnym programie (dla wybranych wartości m). import javax.swing.*; public class Sumowanie { public static void main(String[] args) { final int LIMIT = 200; int sum = 0; while(sum < LIMIT) { String data = JOptionPane.showInputDialog("Podaj liczbę całkowitą:"); if (data == null) break; sum += Integer.parseInt(data); } System.out.println("Suma: " + sum); System.exit(0); } }
Nieco inne podejście do sterowania wykonaniem pętli while można zrealizować za pomocą tzw. zmiennej sterującej: boolean again = true; while(again) { ..... if (/*warunek zakończenia pętli*/) again = false; }
Ten sam przykład można zapisać za pomoca pętli nieskończonej i z użyciem break: Instrukcja do ... while jest bardzo podobna do while.
Ma ona postać: do ins while (wyr)
Znaczenie ins i wyr jest takie samo jak w przypadku instrukcji while. Jedyna
(zaznaczana zresztą przez zapis) różnica w stosunku do instrukcji while polega
na tym, że warunek określony przez wyrażenie wyr jest sprawdzany po każdym
wykonaniu pętli (instrukcji ins), a nie przed (jak to jest w przypadku while). Pętla while może więc nie wykonać się ani razu, natomiast pętla do ... while zawsze wykona się przynajmniej raz.
Podsumujmy. Pętle while lub do..while stosujemy zwykle wtedy, gdy
kontynuacja działania pętli zależy od jakieś warunku, a liczba iteracji nie
jest z góry znana lub łatwa do określenia.
Należy stworzyć klasę Konto (Account) o podanych charakterystykach i dostarczyć
w niej metody np. o nazwie getMonthsToBalance, która (za pomocą symulacji
miesięcznych zmian konta) pozwala odpowiedzieć po ilu miesiącach suma na
koncie osiągnie podaną jako argument docelową wielkośc Przed lekturą dalszego tekstu proszę spróbować samodzielnie rozwiązać to zadanie. Jedno z możliwych rozwiązań jest następujące: public class Account { private double balance; // stan konta private double monthIncome; // stałe miesięczne wpływy (dochód) private double monthExpend; // stałe miesięczne wydatki private double interest; // stopa oprocentowania (roczna) // Konstruktor public Account(double s, double wpl, double wypl, double p) { balance = s; monthIncome = wpl; monthExpend = wypl; interest = p; } // Metoda - zwraca aktualny stan konta public double getBalance() { return balance; } // Metoda - zwraca liczbę miesięcy potrzebnych // by stan konta osiągnął wartość targetBalance public int getMonthsToBalance(double targetBalance) { int n = 0; // miesiące double diff = targetBalance - balance; // różnica między aktualnym // i docelowym stanem while (diff > 0) { // dopóki jest TA różnica - // symulujemy upływ miesięcy i zmiany konta n++; balance *= (1 + (interest/100)/12); // doliczenie odsetek balance += monthIncome - monthExpend; // dochody, wydatki double prevDiff = diff; // poprzednia różnica diff = targetBalance - balance; // bieżąca różnica if (prevDiff <= diff) return -1; // jeżeli różnica się } // nie zmniejsza - nie ma szans return n; // osiagnięcia docelowego stanu } } // Klasa testująca konto class TestKonta { public static void main(String[] args) { Account ac = new Account(2000, 2400, 1800, 10); double cel = 10000; int m = ac.getMonthsToBalance(cel); System.out.println("Miesiace do osiagniecia co najmniej " + cel + ":"); System.out.println(m + " --- stan konta " + ac.getBalance()); } } Wynik działania programu: Miesiace do osiagniecia co najmniej 10000.0:
13 --- stan konta 10430.006715578125
Klasa jest suto komentowana, zwróćmy więc uwagę tylko na to, że staramy się
tu zabezpieczyć przed nieosiągalnymi docelowymi stanami konta (np. kiedy
wydatki są większe od dochodów, a - w którymś momencie - oprocentowanie nie pokryje tej różnicy,
to docelowy stan, który jest większy od aktualnego nigdy nie zostanie osiagnięty).
Innymi słowy staramy się zapewnić zakończenie pętli while. W tym programie
robimy to sprawdzając w pętli - czy z każdą iteracją różnica pomiędzy stanem
docelowym i aktualnym zmniejsza się. Jeśli nie, to nie ma szansy na osiągnięcie
stanu docelowego i metoda zwraca -1 jako liczbę miesięcy. Przed lekturą dalszego tekstu proszę samodzielnie od podstaw przygotować ostateczną, uwzględniającą uwagi o optymalizacji instrukcji wykonywanych w pętli, wersję metody monthsToBalance i przetestowac jej działanie w klasie Account
Ostatecznie więc metodę monthsToBalance moglibyśmy zapisać następująco. public int getMonthsToBalance(double targetBalance) { double wspOds = (interest/100)/12; double diff = targetBalance - balance; int n = 0; while (diff > 0) { n++; balance += wspOds*balance + monthIncome - monthExpend; double prevDiff = diff; diff = targetBalance - balance; if (diff >= prevDiff) return -1; } return n; }
|