Računalništvo 1 (PRA)

lekcije / Zanka while

Zanka while

Spoznali smo že pogojni stavek if-else, ki nam omogoča, da program izvede ene ali druge ukaze, glede na to ali je dani pogoj izpolnjen. Včasih pa želimo v programu kake ukaze ponavljati, dokler je izpolnjen dani pogoj. To naredimo z zanko.
Java pozna tri vrste zank: zanka while, zanka for in zanka do. Najbolj osnovna je zanka while.
Zanka while ima naslednjo obliko:
while (p) {
  A;
}
To pomeni: "če je izpolnjen pogoj p, izvedi ukaze A, nato ponovi zanko." Shematično to prikažemo takole: Shematski prikaz zanke while Ukazi A se imenujejo telo zanke, p pa je pogoj.

Zanke s preprostimi števci

Kot prvi primer napišimo program, ki na zaslon izpiše vsa cela števila od 1 do 100:
StoStevil.java
1
2
3
4
5
6
7
8
9
public class StoStevil {
    public static void main(String[] args) {
	int i = 1;
	while (i <= 100) {
	    System.out.print(i);
	    i = i + 1;
	}	    
    }
}
> javac StoStevil.java
> java StoStevil
12345678910111213141516171819202122232425262728293031323334353637383940414243444
54647484950515253545556575859606162636465666768697071727374757677787980818283848
58687888990919293949596979899100
Ker smo uporabili ukaz System.out.print, so se števila izpisala eno za drugim brez kakeršnihkoli vmesnih presledkov. Program uporabi spremenljivko i z začetno vrednostjo 1, ki ji v vsaki izvedbi prišteje 1. Ko se vrednost i poveča do 101, se zanka ustavi, ker pogoj i <= 100 ni izpolnjen.
Seveda lahko izvedemo zanko tudi kako drugače. Na primer, lahko bi začeli z drugačno začetno vrednostjo i, ali pa bi v vsakem koraku prišteli 2 namesto 1. Prav tako bi lahko spreminjali pogoj i <= 100.
Oglejmo si še en primer: program, ki izpiše vsa liha števila med -10 in 33 v padajočem vrstnem redu. Tokrat naj se vsako število izpiše v svojo vrstico. Sedaj je treba šteti od začetne vrednosti 33 in v vsakem koraku odšteti2. Pogoj za izvajanje zanke pa je, da mora biti števec i večji ali enak -10.
LihaStevila.java
1
2
3
4
5
6
7
8
9
public class LihaStevila {
    public static void main(String[] args) {
	int i = 33;
	while (i >= -10) {
	    System.out.println(i);
	    i = i - 2;
	}	    
    }
}
> javac LihaStevila.java
> java LihaStevila
33
31
29
27
25
23
21
19
17
15
13
11
9
7
5
3
1
-1
-3
-5
-7
-9
Spoznali bomo še mnogo drugih vrst uporabe zanke while. Na primer, ni nujno, da ima zanka while števec i, s katerim kaj štejemo. Lahko se tudi zgodi, da imamo znotraj zanke while še eno zanko (takim zankam pravimo dvojne ali vgnezdene zanke).
Premisli, kaj počne naslednji program:
Klop.java
1
2
3
4
5
6
7
public class Klop {
    public static void main(String[] args) {
	while (true) {
	    System.out.print("klop pod klopjo, ");
	}	    
    }
}
Če razumete, kako deluje zanka while, potem ste odgovorili, da izpisuje niz "klop pod klopjo" v nedogled:
> javac Klop.java
> java Klop
klop pod klopjo, klop pod klopjo, klop pod klopjo, klop pod klopjo, klop pod klo
pjo, klop pod klopjo, klop pod klopjo, klop pod klopjo, klop pod klopjo, klop po
d klopjo, klop pod klopjo, klop pod klopjo, klop pod klopjo, klop pod klopjo, kl
op pod klopjo, klop pod klopjo, klop pod klopjo, klop pod klopjo, klop pod klopj
o, klop pod klopjo, klop pod klopjo, klop pod klopjo ...
Naloga 1
[rešitev]
Vidimo, da zanko while uporabimo, kadar je treba ponavljati kako zaporedje ukazov. Vedno moramo poskrbeti, da se zanka ustavi (razen če namenoma pišemo program, ki se nikoli ne ustavi).
Oglejmo si še en primer bolj podrobno. Denimo, da želimo s programom izračunati vsoto števil 1 + 2 + 3 + ... + 99 + 100. To lahko naredimo na tri načine:
  1. Ker smo matematiki, vemo, da je 1 + 2 + 3 + ... + n aritmetična vrsta in da je njena vsota enaka n(n+1)/2. Torej je vsota prvih sto števil enaka 101 * 100 / 2 in programa sploh ni treba pisati.
  2. Napišemo program
    VsotaBednaResitev.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    public class VsotaBednaResitev {
        public static void main(String[] args) {
    	int vsota = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 +
    	    11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 +
    	    21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 +
    	    31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 +
    	    41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 +
    	    51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 +
    	    61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 +
    	    71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 
    	    81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 +
    	    91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 + 100;
        }
    }
    To je precej nepraktična rešitev, ki se ne bi obnesla, če bi želeli izračunati večjo vsoto, na primer od 1 do 1000000.
  3. Uporabimo zanko while.
Kako torej napišemo program, ki s pomočjo zanke while sešteje števila od 1 do 100? Tako kot v primeru, ko smo izpisali števila od 1 do 100, v zanki štejemo s spremenljivko i od 1 do 100. Poleg tega pa uvedemo še eno spremenljivko vsota, ki ji v vsakem koraku zanke prištejemo vrednost i. Ko se zanka konča, je želena vsota shranjena v spremenljivki vsota:
Vsota.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Vsota {
    public static void main(String[] args) {

	int i = 1;
	int vsota = 0;

	while (i <= 100) {
	    vsota = vsota + i;
	    i = i + 1;
	}

	System.out.println(vsota);
    }
}
> javac Vsota.java
> java Vsota
5050
Ponovimo še enkrat: spremenljivka vsota je na začetku 0. Po vsakem izvajanju zanke, se k njen prišteje vrednost spremenljivke i. Ker spremenljivka i šteje od 1 do 100, se v vsota nabere seštevek vseh celih števil od 1 do 100.
V naslednji nalogi se naučimo, da lahko uporabimo v programu več zank, eno za drugo.
Naloga 2
Naloga 3
[rešitev]
Naloga 4

Zanke brez števcev

Do sedaj smo videli zanke, ki se ponavljajo v nedogled, in take, ki uporabijo števec. Seveda pa to ni edini način za uporabo zank. Oglejmo si primer zanke, v kateri niso uporabljena števila in števci.
V spremenljivki String s je shranjen niz znakov. Napraviti želimo nov niz String t, ki je tak kot niz s brez presledkov na začetku. Na primer, če je niz s enak "   Trije presledki na zacetku.", potem mora biti t enak "Trije presledki na zacetku."
Pri pisanju programa uporabimo metodo substring, ki vrne podniz danega niza:
s.substring(i,j) vrni podniz s od i-tega do (j-1)-tega znaka
Primer uporabe:
PrimerSubstring.java
1
2
3
4
5
6
7
public class PrimerSubstring {
    public static void main(String[] args) {
	String a = "Od nekdaj lepe so Ljubljanke slovele.";
	String b = a.substring(6, 20);
	System.out.println(b);
    }
}
> java PrimerSubstring
daj lepe so Lj
Naš program sestoji iz ene zanke, ki v vsakem koraku odstrani prvi znak iz niza t. Začetna vrednost t je enaka s. Zanka se ustavi, ko postane t prazen niz, ali pa prvi znak ni presledek.
OdstraniPresledke.java
1
2
3
4
5
6
7
8
9
10
11
public class OdstraniPresledke {
    public static void main(String[] args) {
	String s = "   Trije presledki na zacetku.";
	String t = s;
	while (!t.equals("") && t.charAt(0) == ' ') {
	    t = t.substring(1, t.length());
	}
	System.out.println(s);
	System.out.println(t);
    }
}
> java OdstraniPresledke
   Trije presledki na zacetku.
Trije presledki na zacetku.
Naloga 5

Vgnezdene zanke

Oglejmo si sedaj, kako lahko vgnezdimo eno zanko v drugo. Denimo, da želimo izpisati na zaslon pet vrstic sestavljenih iz desetih zvezdic, ločenih s presledki:
* * * * * * * * * *
* * * * * * * * * *
* * * * * * * * * *
* * * * * * * * * *
* * * * * * * * * *
Prvi način:
Zvezdice1.java
1
2
3
4
5
6
7
8
9
public class Zvezdice1 {
    public static void main(String[] args) {
	System.out.println("* * * * * * * * * *");
	System.out.println("* * * * * * * * * *");
	System.out.println("* * * * * * * * * *");
	System.out.println("* * * * * * * * * *");
	System.out.println("* * * * * * * * * *");
    }
}
Seveda se tak program ne obnese, če želimo izpisati 10000 vrstic po 10 zvezdic. Bolje je, če uporabimo zanko while:
Zvezdice2.java
1
2
3
4
5
6
7
8
9
public class Zvezdice2 {
    public static void main(String[] args) {
	int i = 0;
	while (i < 10) {
	    System.out.println("* * * * * * * * * *");
	    i = i + 1;
	}
    }
}
Ta rešitev je že boljša, vendar se ne bi obnesla, če bi želeli izpisati 10 vrstic po 10000 zvezdic. Program spremenimo tako, da vsako vrstico izpiše s pomočjo zanke while. Kako se to naredi, smo že videli v
prejšnji vaji.
Zvezdice3.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Zvezdice3 {
    public static void main(String[] args) {
	int i = 0;
	while (i < 10) {                // 10 vrstic zvezdic
	    int j = 0;
	    while (j < 9) {             // 9 zvezdic s presledkom
		System.out.print("* ");
		j = j + 1;
	    }
	    System.out.println("*");    // zadnja zvezdica in pomik v novo vrsto
	    i = i + 1;
	}
    }
}
Vidimo primer vgnezdene zanke, saj imamo znotraj ene zanke while še eno zanko while.
Naloga 6
Kolikokrat se v programu Zvezdice3.java izvede 7. vrstica in kolikokrat 10. vrstica, ko ga poženemo?
Namig
Uporabi Eclipse ali DrJava in poženi program korakoma.
[rešitev]
V naslednjem primeru si oglejmo, kaj se zgodi, če želimo v vsaki vrstici izpisati kaj drugega. Na primer, da želimo s pomočjo dvojne zanke izpisati na zaslon
1 
1 2 
1 2 3 
1 2 3 4 
1 2 3 4 5 
1 2 3 4 5 6 
1 2 3 4 5 6 7 
1 2 3 4 5 6 7 8 
1 2 3 4 5 6 7 8 9
Ker želimo izpisati 9 vrstic, bo program sestavljen iz zanke, ki šteje od 1 do 9:
1
2
3
4
5
int i = 1;
while (i <= 9) {
   // tukaj napišemo še eno zanko
   i = i + 1;
}
Kaj pa naj se izvede v notranji zanki? V i-ti vrstici moramo izpisati števila od 1 do i. To pa spet naredimo z zanko, ki šteje od 1 do i. To nas privede do naslednjega programa:
ZankaZanka.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ZankaZanka {
    public static void main(String[] args) {

	int i = 1;
	while (i < 10) {
	    int j = 1;
	    while (j <= i) {
		System.out.print(j);
		System.out.print(" ");
		j = j + 1;
	    }
	    System.out.println();
	    i = i + 1;
	}
    }
}
> javac ZankaZanka.java
> java ZankaZanka
1 
1 2 
1 2 3 
1 2 3 4 
1 2 3 4 5 
1 2 3 4 5 6 
1 2 3 4 5 6 7 
1 2 3 4 5 6 7 8 
1 2 3 4 5 6 7 8 9
Naloga 7

Zanke z dodatnim pogojem za ustavljanje

Včasih želimo, da bi se izvajanje zanke prekinilo, čeprav števec še ni preštel do konca. To lahko naredimo s pomožno spremenljivko tipa boolean, ki jo v telesu zanke nastavimo na false, če je treba zanko prekiniti. Pogoj zanke nato upošteva vrednost spremenljivke.
Za primer vzemimo naslednjo nalogo. Denimo, da želimo poiskati najmanjše pozitivno celo število med 1 in 100, ki zadošča enačbi
x2 − 21 x = 882.
Če tako število ne obstaja, potem izpišemo na zaslon "Ni resitve med 1 in 100."
Treba je torej izračunati dva podatka: ali rešitev obstaja (true ali false) in kolikšna je rešitev (celo število). Torej imam dve spremenljivki, vsako za en podatek, ki ga je treba izračunati. Prva spremenljivka je boolean obstaja, ki se mora nastaviti na true, če rešitev obstaja. Druga spremenljivka je int x, ki vsebuje rešitev enačbe, v primeru da rešitev obstaja.
Zanka, ki išče rešitev po vrsti pregleda vse vrednosti x med 1 in 100 in preveri, ali katero od njih zadošča enačbi. Če kako število zadošca enačbi, nastavi obstaja na true in neha izvajati zanko. Na koncu rešitev še izpišemo, da vidimo, kaj se je zgodilo.
IsciResitev.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class IsciResitev {
    public static void main(String[] args) {
	int x = 1;
	boolean obstaja = false;

	while (!obstaja && (x <= 100)) {
	    if (x * x - 21 * x == 882) {
		obstaja = true;
	    }
	    else {
		x = x + 1;
	    }
	}

	if (obstaja) {
	    System.out.print("Resitev: x = ");
	    System.out.println(x);
	}
	else {
	    System.out.println("Ni resitve med 1 in 100.");
	}
    }
}
Če izvajanje programa pride v 8. vrstico, se obstaja nastavi na true in zanka neha izvajati, saj njen pogoj zahteva, da mora veljati !obstaja.
> javac IsciResitev.java
> java IsciResitev
Resitev: x = 42

Vaje za ponavljanje

Naloga 8
Namig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Denar {
  public static void main(String[] args) {
    int k = tu vstavi zacetno vrednost;
    int k10, k5, k2, k1;

    tu napisi svoj program

    System.out.print(k + " tolarjev = ");
    System.out.print(k10 + " kovancev za 10 + ");
    System.out.print(k5 + " kovancev za 5 + ");
    System.out.print(k2 + " kovancev za 2 + ");
    System.out.println(k1 + " kovancev za 1 tolar.");
  }
}
Naloga 9: Kvadratna enačba
Naloga 10: Opis temperature
Naloga 11

Naloge s predavanj in vaj

Naloga 12: Vsota števil
Naloga 13: Vsota aritmetične vrste
Naloga 14: Vsota števil
Naloga 15: Vsota geometrijske vrste
Naloga 16: Vsota Taylorjeve vrste
Naloga 17
Naloga 18
Naloga 19
Naloga 20

Rešitve nalog

Naloga 1

Program se takoj ustavi, saj pogoj v zanki while že na začetku ni izpolnjen.

Naloga 3

Zvezdice.java
1
2
3
4
5
6
7
8
9
10
11
public class Zvezdice {
    public static void main(String[] args) {
	int n = 20;
	int i = 0;
	while (i < n - 1) {
	    System.out.print("* ");
	    i = i + 1;
	}
	System.out.println("*");
    }
}
> java Zvezdice
* * * * * * * * * * * * * * * * * * * *

Naloga 6

Vsakič, ko se izvede zunanja zanka, se notranja zanka izvede devetkrat. Ker se zunanja zanka izvede desetkrat, se 7. vrstica, ki je v telesu notranje zanke, izvede 90-krat.
10. vrstica je v telesu zunanje zanke in ni v telesu notranje zanke. Zato se izvede 10-krat, saj se zunanja zanka izvede 10-krat.