SEN2001 · 7. hafta · Control Structures

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.

control structureselection statementtwo-way selectordangling elsemultiple-way selectorswitchfall-throughcounter-controlled looploop variablelogically controlled looppretestposttestuser-located loop controlbreakcontinuelabeled breakiteratorgotoguarded commandnondeterminismBöhm-Jacopini

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:

  1. switch’te break yoksa alttaki case’lere düşülür.
  2. C/Java for döngüsünde koşul her turdan önce yeniden hesaplanır.
  3. while 0 kez, do-while en az 1 kez çalışır.
  4. continue turu atlar ama güncelleme (i++) yine çalışır.
  5. 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 Ada end / end if ile 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
DilFall-throughNot
C, C++, Java, JavaScriptVar, break unutulursa sessizce düşerEsneklik (writability)
C#Yok: her dolu segment break, goto case, return ile bitmeliReliability
Ada case, Ruby caseYokTü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ı:

  1. Döngü değişkeninin tipi ve scope’u ne?
  2. Gövdede döngü değişkeni veya parametreleri değiştirilebilir mi, tur sayısını etkiler mi?
  3. Parametreler bir kez mi, her turda mı hesaplanır?
  4. Döngü bittikten sonra değişkenin değeri tanımlı mı?
DilCevaplar
Ada for I in 1..N loopI ö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 continue switch’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 biterbreak yoksa alttakiler de çalışır
C#‘ta break’siz case derlenirDerlenmez (dolu segment)
for’da koşul bir kez hesaplanırC/Java’da her turda; Ada’da bir kez
continue döngüden çıkarTuru atlar; çıkmak için break
do-while 0 kez çalışabilirEn az 1 kez
for (int v : arr) v *= 2; diziyi değiştirirv 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ç.

Soru 1orta

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
142

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.

Soru 2orta

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
4

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.

Soru 3kolay

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
37

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.

Soru 4kolay

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
0 1

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.

Soru 5orta

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
5 5

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.

Soru 6orta

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
6

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.

Soru 7kolay

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
BC

"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.

Soru 8orta

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
medium; the inner if

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ı.

Soru 9kolay

Using Python's semantics, what values does range(2, 15, 4) produce? And range(10, 0, -3)?

Cevabı göster
2, 6, 10, 14 and 10, 7, 4, 1

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.

Soru 10orta

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
fi
Cevabı göster
x = 3, y = 3: either one (nondeterministic), max = 3; x = 1, y = 4: only max := y

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.

Soru 11zor

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)
od
Cevabı göster
1, 2, 3, 4; no, every execution path ends sorted

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.

Soru 12kolay

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
3 3

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.

Deftere not kartı

Böhm-Jacopini: sequence + selection + pretest loop yeter, goto gerekmez koşul: C89 aritmetik kabul eder, Java/C# boolean ister dangling else: Java/C → en yakın if; Python girinti; Ruby/Ada end; Perl {} switch: break yoksa ALTA DÜŞER (fall-through). C# yasaklar (break/goto şart) boş case'ler art arda serbest: case 1: case 2: C for (e1; e2; e3): e1 bir kez, e2 HER TURDAN ÖNCE, gövde, e3. gövde n'yi değiştirebilir Ada for I in 1..N: aralık bir kez, I gövdede atanamaz, scope döngü while = pretest (0+), do-while = posttest (1+) break: döngüden çık. continue: turu atla (e3 yine çalışır). labeled: Java/C#/Perl switch içinde continue → en yakın döngüye uygulanır enhanced for (v : arr): v kopya, diziyi değiştirmez Python range(a, b, s): a dahil, b hariç guarded if: hepsi hesaplanır, çok true → nondeterministic, hiç true → HATA guarded do: hiç true kalmayınca biter