1. Giriş
Sualdan başlayaq: ümumiyyətlə nəyə görə bir dövrü başqa birinin içinə yerləşdirmək lazımdır? Məsələ ondadır ki, çox vaxt verilənlərimiz və ya tapşırıqlarımız tək xətt üzrə deyil, məsələn, cədvəl, şəbəkə və ya hətta çoxölçülü struktur şəklində təşkil olunur. Tutaq ki, ekrana vurma cədvəlini çıxarmaq, ikiölçülü massiv üzrə keçmək və ya elementlərin bütün cütləri arasındakı kəsişmələri hesablamaq istəyirsiniz. Burada bir dövr açıq-aşkar yetməyəcək — içində daha bir dövr lazım olacaq.
Proqramlaşdırmada iç-içə dövr iki zəngli saat kimidir: xarici olan işə düşür və onun daxilində daha biri start götürür; birincisi aktiv qaldığı müddətdə o hər dəfə çalacaq. Beləcə, bir “xarici” iterasiya davam edərkən, daxili öz marşrutunu tam keçir (və bunu hər bir xarici iterasiya üçün təkrar-təkrar edir).
Yaxşı nümunə — saatlar və dəqiqələr. Saatlar — bu, 0-dan 23-ə qədər xarici dövr, dəqiqələr — 0-dan 59-a qədər daxili dövrdür. Xarici dövrün hər dəyişməsində daxili öz bütün dəyərlərini qaçıb bitirir.
2. İç-içə dövrlərin sintaksisi
Java-da iç-içə dövrlərin sintaksisi adi dövrlərdən fərqlənmir — sadəcə bir dövrü daxilə, yəni digərinin bədəninin içinə yazırsınız. Gəlin for və while ilə nümunələrə baxaq:
// Xarici for dövrü
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++) // Daxili for dövrü
{
System.out.print(i + "," + j + " ");
}
System.out.println(); // Daxili dövrdən sonra yeni sətirə keçid
}
Burada xarici dövr i dəyişənini idarə edir (0-dan 2-yə qədər), daxili isə — j dəyişənini (0-dan 3-ə qədər). Hər bir i dəyəri üçün daxili dövr tam olaraq j == 0-dan j == 3-ə qədər keçir. Bu kodu işlətsəniz, xoş bir koordinat cədvəli görəcəksiniz:
0,0 0,1 0,2 0,3
1,0 1,1 1,2 1,3
2,0 2,1 2,2 2,3
while istifadə edilən oxşar nümunə:
int i = 0;
while (i < 3)
{
int j = 0;
while (j < 4)
{
System.out.print(i + "," + j + " ");
j++;
}
System.out.println();
i++;
}
Diqqət yetirin: xarici dövrün hər keçidi daxilində daxili dövrün dəyişəni (j) yenidən inisializasiya olunmalıdır, əks halda yalnız bir sətir görəcəyik!
3. İç-içə dövrlərin işləməsinə nümunələr
Nümunə 1: şahmat taxtasının (8x8) çıxışı
Gəlin ilk tapşırığımız — ekrana klassik şahmat taxtasını qara və ağ xanalar şəklində çıxarmaq olsun (güman edək ki, ‘#’ — qara, ‘.’ — ağdır). Bunu iç-içə for dövrləri ilə həyata keçirək:
for (int row = 0; row < 8; row++)
{
for (int col = 0; col < 8; col++)
{
// Əgər sətir və sütun nömrələrinin cəmi cütdürsə — xana ağ, əks halda qaradır
if ((row + col) % 2 == 0)
System.out.print("_");
else
System.out.print("#");
}
System.out.println(); // Hər sətrdən sonra sətir sonu
}
Nəticə:
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
Vacib məqam: yuvalanma onu təmin edir ki, hər bir sətir (row) üçün bütün sütunlar (col) üzrə tam keçək. Yuvalanma olmasa, taxta quruluşunu almazdıq — yalnız bir sətir və ya bir sütun olardı.
Nümunə 2: vurma cədvəli
İç-içə dövrlər üçün klassik namizədlərdən biri! Gəlin 1–9 vurma cədvəlini çıxaraq:
for (int i = 1; i <= 9; i++)
{
for (int j = 1; j <= 9; j++)
{
System.out.print(i * j + "\t");
}
System.out.println();
}
Formatlaşdırma i * j + "\t" cədvəlin səliqəli görünməsi üçün tab ayırıcıları əlavə edir.
Nəticə:
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
...
9 18 27 36 45 54 63 72 81
4. İç-içə dövrlər və onların idarə edilməsi — incəliklər
break və continue-nun iç-içə dövrlərdə təsiri haqqında
Məhz burada bir çox yeni başlayanlar çaşır! Əgər siz break və ya continue-dan daxili dövrdə istifadə etsəniz, onlar yalnız həmin dövrə təsir edir. Xarici dövr isə sanki heç nə olmamış kimi işini davam etdirir.
Nümunə: yalnız daxili dövrdən vaxtından əvvəl çıxış
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
if (j == 3)
break; // yalnız daxili dövrdən çıxırıq!
System.out.print(i + "," + j + " ");
}
System.out.println();
}
Nəticə:
0,0 0,1 0,2
1,0 1,1 1,2
2,0 2,1 2,2
Əgər iki iç-içə dövrün hər ikisindən dərhal çıxmaq lazımdırsa (məsələn, cədvəldə ilk uğurlu uyğunluqda axtarışı təcili dayandırmaq), adətən bayraq (flag) istifadə olunur və ya xüsusi fənd tətbiq edilir (məsələn, hər şey funksiyanın daxilindədirsə, return).
5. İç-içə dövrlərin vizuallaşdırılması
Bəzən iç-içə dövrlərin icra ardıcıllığını “göz önünə gətirmək” çətin olur. Gəlin aşağıdakı blok-sxemə baxaq:
Cədvəl şəklində — i 1-dən 3-ə qədər, j 1-dən 4-ə qədər olduqda ümumilikdə neçə iterasiya olacaq?
| i | j (hər i üçün ardıcıl götürülür) | Daxili dövrün iterasiyaları |
|---|---|---|
| 1 | 1, 2, 3, 4 | 4 |
| 2 | 1, 2, 3, 4 | 4 |
| 3 | 1, 2, 3, 4 | 4 |
| Cəmi: 3 × 4 = 12 |
6. İç-içə dövrlərlə işləyərkən səhvlər və gizli tələlər
Yayılmış bir səhv — daxili dövrün dəyişənini səhv inisializasiya etməkdir. Məsələn, onu xarici dövrdən kənarda elan edib hər addımda sıfırlamamaq. Nəticədə daxili dövr ümumiyyətlə işə düşməyə və ya düzgün işləməməyə bilər.
int j = 0;
for (int i = 0; i < 3; i++)
{
while (j < 4) // Ay! Birinci iterasiyadan sonra j artıq 4 ola bilərdi.
{
System.out.print(i + "," + j + " ");
j++;
}
System.out.println();
}
Burada dövr yalnız bir dəfə işləyəcək. Daxili dövrlərin dəyişənlərini xaricilərin daxilində inisializasiya etməyi unutmayın!
Həmçinin, təsadüfən eyni adlı dəyişənlərlə iki iç-içə dövr yazsanız (for (int i = 0; ...) { for (int i = 0; ...) { ... } }), kompilyator sizi danlayacaq: dəyişən artıq müəyyən edilib.
GO TO FULL VERSION