Kontrol yapıları: seçim, döngüler ve guarded commands
İki yönlü ve çok yönlü seçim (switch fall-through), counter-controlled ve logically-controlled döngüler, break/continue, iterator döngüleri, goto ve Dijkstra'nın guarded command'ları. Çıktı izleme soruları burada.
Kontrol yapıları (control structures), programın hangi ifadeyi ne zaman çalıştıracağını belirler. Teoride bunun için çok az şey gerekir: Böhm ve Jacopini (1966), sıralama (sequence), seçim (selection) ve ön-testli döngü (pretest logical loop) ile her algoritmanın yazılabileceğini gösterdi; goto gerekmez. Dillerin bundan fazlasını sunmasının nedeni writability ve readability. Sınavda en çok switch/döngü çıktısı ve dil tasarım farkları sorulur.
Altın kurallar:
- switch’te break yoksa alttaki case’lere düşülür.
- C/Java for döngüsünde koşul her turdan önce yeniden hesaplanır.
- while 0 kez, do-while en az 1 kez çalışır.
- continue turu atlar ama güncelleme (
i++) yine çalışır. - Guarded command’da birden çok guard true ise seçim nondeterministic.
Seçim (selection)
İki yönlü seçim
Tasarım soruları: koşul ifadesinin tipi ne olabilir? then/else kısmı tek ifade mi, blok mu? İç içe seçimin anlamı nasıl belirlenir?
- C89 ve C++ koşul olarak aritmetik ifade kabul eder (0 false). Java ve C# boolean ister.
- Dangling else:
if (a) if (b) s1; else s2;else hangi if’e ait? Java, C, C++, C#: en yakın eşleşmemiş if. Perl her dalda süslü parantez zorunlu tutar; Python girintiyle, Ruby ve Adaend/end ifile belirler.
int y = 7;
if (y > 5)
if (y > 10) System.out.println("big");
else System.out.println("medium");
medium
Çok yönlü seçim: switch
int t = 0;
for (int k = 0; k < 5; k++) {
switch (k % 3) {
case 0: t += 1; // break yok: case 1'e düşer
case 1: t += 10; break;
default: t += 100;
}
}
System.out.println(t);
142
k k%3 çalışanlar t
0 0 +1, +10 (düştü) 11
1 1 +10 21
2 2 default +100 121
3 0 +1, +10 132
4 1 +10 142
| Dil | Fall-through | Not |
|---|---|---|
| C, C++, Java, JavaScript | Var, break unutulursa sessizce düşer | Esneklik (writability) |
| C# | Yok: her dolu segment break, goto case, return ile bitmeli | Reliability |
Ada case, Ruby case | Yok | Tüm değerler kapsanmalı (Ada) |
Boş case’leri art arda yazmak (case 1: case 2:) her dilde serbesttir. Çok yönlü seçim her zaman iç içe else if ile de yazılabilir; Python’da elif, Python 3.10’dan beri match.
Counter-controlled döngüler
Döngü değişkeni (loop variable) bir başlangıç, bitiş ve adım değeriyle (loop parameters) kontrol edilir. Tasarım soruları:
- Döngü değişkeninin tipi ve scope’u ne?
- Gövdede döngü değişkeni veya parametreleri değiştirilebilir mi, tur sayısını etkiler mi?
- Parametreler bir kez mi, her turda mı hesaplanır?
- Döngü bittikten sonra değişkenin değeri tanımlı mı?
| Dil | Cevaplar |
|---|---|
Ada for I in 1..N loop | I örtük bildirilir, scope = döngü, gövdede atanamaz, aralık bir kez hesaplanır |
C for (e1; e2; e3) | Açık döngü değişkeni yok, her şey gövdede değişebilir, e2 ve e3 her turda hesaplanır, üçü de boş olabilir: for (;;) |
| Java, C# | C gibi, ama e2 boolean olmalı |
Python for x in range(...) | Aslında iterator döngüsü; opsiyonel else döngü break’siz biterse çalışır |
int n = 10, iters = 0;
for (int i = 0; i < n; i++) { n--; iters++; }
System.out.println(iters + " " + n);
5 5
C/Java for’unu while’a çevirme tarifi (operational semantics’teki gibi):
e1;
while (e2) {
body;
e3;
}
Fark: for’un başında bildirilen değişkenin scope’u döngüyle sınırlıdır; while versiyonunda dışarıda kalır. Ayrıca gövdedeki continue for’da e3’e gider, bu çeviride e3’ü atlar.
Logically-controlled döngüler
int a = 10, w = 0, d = 0;
while (a < 10) { w++; a++; } // pretest
a = 10;
do { d++; a++; } while (a < 10); // posttest
System.out.println(w + " " + d);
0 1
Pretest (while) gövdeyi hiç çalıştırmayabilir; posttest (do-while) en az bir kez çalıştırır. Java’da posttest döngünün tersi yoktur; Pascal repeat ... until koşul true olunca durur.
Kullanıcı kontrollü çıkış: break ve continue
int count = 0;
outer:
for (int i = 1; i <= 4; i++) {
for (int j = 1; j <= 4; j++) {
if (j == 3) continue outer;
if (i == 3) break outer;
count++;
}
}
System.out.println(count);
4
break: en içteki döngüden (ya da switch’ten) çıkar.continue: o turun geri kalanını atlar, döngü sürer.- Etiketli (labeled)
break/continue: Java, C#, Perl (last/next). C’de yok. - switch içindeki
continueswitch’e değil en yakın döngüye uygulanır.
Etiketli break, goto’nun “kontrollü” halidir: sadece döngüden dışarı, bilinen bir noktaya atlar.
Iterator tabanlı döngüler
Bir veri yapısının elemanları üzerinde, yapının kendi sırasıyla gezinir: Java for (int v : arr), C# foreach, Python for x in seq, JavaScript for (const x of arr).
int[] arr = {1, 2, 3};
for (int v : arr) { v *= 2; }
System.out.println(arr[0] + arr[1] + arr[2]);
6
v her turda elemanın kopyası. Diziyi değiştirmek için indisli döngü gerekir.
goto
En güçlü ve en tehlikeli kontrol ifadesi. Kısıtsız goto okunabilirliği öldürür: kodu okuyan, kontrolün nereden geldiğini bilemez. Dijkstra 1968’de “Go To Statement Considered Harmful” yazdı. Java, Python ve Ruby’de goto yok. C# goto’ya izin verir, en meşru kullanımı switch içinde goto case. Yapısal programlama ilkesi: her yapının tek girişi ve (mümkünse) tek çıkışı olsun.
Guarded commands (Dijkstra, 1975)
Amaç: geliştirme sırasında doğrulamayı (verification) kolaylaştırmak. İki yapı var.
Seçim:
if <guard1> -> <stmt1>
[] <guard2> -> <stmt2>
...
fi
Tüm guard’lar hesaplanır. Birden fazlası true ise biri nondeterministic seçilir. Hiçbiri true değilse çalışma zamanı hatası.
if x >= y -> max := x
[] y >= x -> max := y
fi
x = y iken iki guard da true, hangisi seçilirse seçilsin sonuç aynı. Programcı “eşitlikte hangisini seçeyim?” kararını vermek zorunda değil.
Döngü:
do q1 > q2 -> swap(q1, q2)
[] q2 > q3 -> swap(q2, q3)
[] q3 > q4 -> swap(q3, q4)
od
Her turda true olan guard’lardan biri seçilir; hiçbiri true kalmayınca biter. Bitişte q1 ≤ q2 ≤ q3 ≤ q4 olduğu kesin. Başlangıç 4, 3, 2, 1 için olası tüm seçim yollarını tek tek denesen bile hepsi 1, 2, 3, 4’te biter. Ayrıca en büyük ortak bölen:
do x > y -> x := x - y
[] y > x -> y := y - x
od x = 18, y = 12 → 6
Sık yapılan hatalar
| Yanlış | Doğrusu |
|---|---|
| Eşleşen case çalışınca switch biter | break yoksa alttakiler de çalışır |
| C#‘ta break’siz case derlenir | Derlenmez (dolu segment) |
| for’da koşul bir kez hesaplanır | C/Java’da her turda; Ada’da bir kez |
| continue döngüden çıkar | Turu atlar; çıkmak için break |
| do-while 0 kez çalışabilir | En az 1 kez |
for (int v : arr) v *= 2; diziyi değiştirir | v kopya, değiştirmez |
| Guarded if’te hiç guard true değilse atlanır | Çalışma zamanı hatası |
| Guarded do’da sonuç seçime bağlı | Sıralama örneğinde her yol aynı sonucu verir |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
What is printed in Java?
int t = 0;
for (int k = 0; k < 5; k++) {
switch (k % 3) {
case 0: t += 1;
case 1: t += 10; break;
default: t += 100;
}
}
System.out.println(t);Cevabı göster
k % 3 sırasıyla 0, 1, 2, 0, 1. Case 0'da break yok, case 1'e düşer: +11. Case 1: +10. Default: +100.
Toplam: 11 + 10 + 100 + 11 + 10 = 142.
What is printed in Java?
int count = 0;
outer:
for (int i = 1; i <= 4; i++) {
for (int j = 1; j <= 4; j++) {
if (j == 3) continue outer;
if (i == 3) break outer;
count++;
}
}
System.out.println(count);Cevabı göster
i = 1: j = 1, 2 sayılır, j = 3'te continue outer dış döngünün bir sonraki turuna atlar. i = 2: yine 2 sayım. i = 3, j = 1: j == 3 değil, i == 3 → break outer iki döngüden birden çıkar. Toplam 4.
What is printed in Java?
int sum = 0;
for (int i = 1; i <= 10; i++) {
if (i % 3 == 0) continue;
sum += i;
}
System.out.println(sum);Cevabı göster
continue o turun geri kalanını atlar ama döngü sürer (i++ yine çalışır). 1..10 toplamı 55; atlanan 3 + 6 + 9 = 18. 55 - 18 = 37.
What is printed? How many times does each body run?
int a = 10, w = 0, d = 0;
while (a < 10) { w++; a++; }
a = 10;
do { d++; a++; } while (a < 10);
System.out.println(w + " " + d);Cevabı göster
while pretest'tir: koşul baştan false, gövde hiç çalışmaz. do-while posttest'tir: gövde önce bir kez çalışır, sonra koşul (11 < 10) false olur. Posttest döngü en az bir kez çalışır.
How many iterations does the loop perform, and what is n afterwards?
int n = 10, iters = 0;
for (int i = 0; i < n; i++) { n--; iters++; }
System.out.println(iters + " " + n);Cevabı göster
Java ve C'nin for'unda koşul (i < n) her turdan önce yeniden hesaplanır ve gövde n'yi değiştirebilir. i: 0,1,2,3,4 iken n: 10,9,8,7,6 → koşul tutar. Beşinci turdan sonra i = 5, n = 5: 5 < 5 false. Ada'nın for I in 1..N döngüsünde aralık bir kez hesaplanır, gövdedeki değişiklik tur sayısını etkilemez.
What is printed in Java?
int[] arr = {1, 2, 3};
for (int v : arr) { v *= 2; }
System.out.println(arr[0] + arr[1] + arr[2]);Cevabı göster
Iterator tabanlı (enhanced) for'da v her turda elemanın bir kopyasını alır. v'yi değiştirmek diziyi değiştirmez: 1 + 2 + 3 = 6.
What is printed in Java?
String s = "b";
switch (s) {
case "a": System.out.print("A");
case "b": System.out.print("B");
case "c": System.out.print("C"); break;
default: System.out.print("D");
}Cevabı göster
"b" eşleşir, B basılır. break olmadığı için case "c"'ye düşer: C. Orada break var, default'a ulaşılmaz. C#'ta case "a" ve case "b" segmentleri break'siz olduğu için derlenmezdi.
What is printed in Java? To which if does the else belong?
int y = 7;
if (y > 5)
if (y > 10) System.out.println("big");
else System.out.println("medium");Cevabı göster
Java'da else en yakın eşleşmemiş if'e bağlanır (girinti önemsiz). Dış koşul true, iç koşul false → else çalışır. Else dıştaki if'e ait olsaydı (y > 5 true olduğu için) hiçbir şey basılmazdı.
Using Python's semantics, what values does range(2, 15, 4) produce? And range(10, 0, -3)?
Cevabı göster
range(başlangıç, bitiş, adım): başlangıç dahil, bitiş hariç. 2, 6, 10, 14; 18 ≥ 15 olduğu için durur. Negatif adımda bitişten büyük kaldıkça devam: 10, 7, 4, 1; -2 > 0 değil, durur.
For the guarded selection below, which assignments may execute when x = 3, y = 3? What if x = 1, y = 4?
if x >= y -> max := x
[] y >= x -> max := y
fiCevabı göster
Guarded command'da tüm guard'lar hesaplanır. Birden fazlası true ise biri nondeterministic seçilir; burada iki seçim de aynı sonucu verir. x = 1, y = 4'te sadece ikinci guard true. Hiçbiri true olmasaydı çalışma zamanı hatası olurdu.
Starting from q1 = 4, q2 = 3, q3 = 2, q4 = 1, what are the final values after the guarded loop? Does the order of choices matter?
do q1 > q2 -> swap(q1, q2)
[] q2 > q3 -> swap(q2, q3)
[] q3 > q4 -> swap(q3, q4)
odCevabı göster
do-od döngüsü her turda açık guard'lardan birini rastgele seçer ve hiçbir guard true kalmayınca biter. Bitiş koşulu q1 ≤ q2 ≤ q3 ≤ q4, yani sıralı. Seçim sırası adım sayısını değiştirebilir ama sonucu değiştirmez. Dijkstra'nın amacı buydu: sonucu kanıtlarken sırayla uğraşmamak.
What is printed in Java?
int z = 100, steps = 0;
while (true) {
z /= 3;
steps++;
if (z < 5) break;
}
System.out.println(steps + " " + z);Cevabı göster
User-located loop control: çıkış noktası gövdenin ortasında. z: 100 → 33 → 11 → 3 (int bölme). Üçüncü turda 3 < 5, break. Böyle döngüler mid-test döngü yazmanın yoludur.
Doğrulama: Tüm Java çıktıları JDK ile çalıştırıldı (java verify/SEN2001/W07.java). Guarded command sorularında node ile her adımda açık olan tüm guard'lar denenerek bütün olası nondeterministic çalışma yolları gezildi ve son durumlar listelendi (verify/SEN2001/07.mjs). Python kurulu olmadığı için range örnekleri aynı kuralı uygulayan bir JavaScript fonksiyonuyla üretildi.