Ders 05 / 14
Denetim Akışı
PHP'de karşılıksız kalan dalların davranışı: switch ile match arasındaki fark, gevşek ve katı karşılaştırma, döngü değişkenlerinin ömrü, foreach ve referans tuzağı ve match içinde default kolunun durdurmayı sessize çevirmesi.
Yayın Güncelleme
Bu derste
- Karşılıksız kalan bir switch ile match dalının sonucunu ölçerek ayırt eder
- switch'in gevşek, match'in katı karşılaştırma yaptığını aynı girdilerle gösterir
- İç içe döngülerde break ve continue düzeylerinin sonucu nasıl değiştirdiğini doğrular
- foreach ile referans kullanımının dizide bıraktığı kalıntıyı ölçer ve nasıl temizlendiğini gösterir
İçindekiler
İşleçler dersi, tipleri buluşturan işleçlerin neyi bildirdiğini ve neyi susturduğunu ölçtü. Bu ders aynı soruyu dallara soruyor: bir koşulun, bir durumun ya da bir match kolunun hiçbiri eşleşmediğinde, yani karşılıksız bir dal kaldığında, dil ne yapıyor? Cevap iki deyimde farklıdır ve bu dersin ölçü sınıfı durdurur: en az bir deyim, eşleşme yoksa yürütmeyi keser.
PHP’de koşullu dallanma için if, elseif ve else, çoklu seçim için switch ve match, tekrar için for, while, do while ve foreach vardır. Programlama Temelleri kursunda (M01/K02) bu yapıların mantığı kurulmuştu; burada yalnız PHP’nin eşleşme ve çıkış davranışı ölçülüyor. Her dosya bir hata işleyicisi kurar ve işleyici, sinyal gelirse onu görünür kılar.
Karşılıksız Dal: switch ve match
switch bir değeri sırayla case etiketleriyle karşılaştırır ve hiçbiri eşleşmezse, default yoksa, hiçbir şey yapmadan geçer. match aynı işi bir ifade olarak yapar ve bir değer döndürür; hiçbir kol eşleşmezse bir UnhandledMatchError fırlatır. Aşağıdaki dosya üç değeri iki deyimden geçirir. $sonuc değişkeni her turda önceki bir değerle başlar; karşılıksız dalda bu değerin ne olduğu ölçülür.
<?php // karsiliksiz.php — switch sessizce gecer, match durdurur $bildirim = 0; set_error_handler(function (int $seviye) use (&$bildirim): bool { $bildirim++; return true; }); foreach ([1, 2, 3] as $kod) { $sonuc = 'onceki'; switch ($kod) { case 1: $sonuc = 'bir'; break; case 2: $sonuc = 'iki'; break; } echo "switch($kod): $sonuc\n"; $sonuc = 'onceki'; try { $sonuc = match ($kod) { 1 => 'bir', 2 => 'iki', }; echo "match($kod): $sonuc\n"; } catch (UnhandledMatchError $hata) { echo "match($kod): ", get_class($hata), ' (sonuc: ', $sonuc, ")\n"; } } echo 'bildirim: ', $bildirim, "\n";
switch(1): bir match(1): bir switch(2): iki match(2): iki switch(3): onceki match(3): UnhandledMatchError (sonuc: onceki) bildirim: 0
KN34. Üçüncü turda switch hiçbir şey yapmadı: değişken önceki değerinde kaldı ve bildirim sayacı artmadı. match aynı girdide UnhandledMatchError fırlattı ve atama gerçekleşmediği için $sonuc yine önceki değerinde kaldı; fark, dilin bu durumda artık sessiz kalmamasıdır. match ifadesi, eşleşmeyen bir değeri bir hata olarak tanımlar; switch ise bunu normal bir yürütme yolu sayar.
Gevşek ve Katı Karşılaştırma
İkisi arasındaki ikinci fark, karşılaştırmanın sıkılığıdır. switch etiketleri == ile, yani gevşek eşitlikle karşılaştırır; match kolları === ile, yani katı eşitlikle. Aynı girdi iki deyimde farklı kola gidebilir. Aşağıdaki dosya beş girdiyi iki deyime verir; etiket ve kol listeleri aynıdır.
<?php // karsilastirma.php — switch gevsek, match kati karsilastirir $girdiler = [ ["'1'", '1'], ['null', null], ["''", ''], ['0', 0], ["'a'", 'a'], ]; printf("%-6s%-13s%s\n", 'girdi', 'switch', 'match'); foreach ($girdiler as [$ad, $girdi]) { switch ($girdi) { case 1: $s = 'case 1'; break; case false: $s = 'case false'; break; case 'a': $s = "case 'a'"; break; default: $s = 'default'; } $m = match ($girdi) { 1 => 'kol 1', false => 'kol false', 'a' => "kol 'a'", default => 'default', }; printf("%-6s%-13s%s\n", $ad, $s, $m); }
girdi switch match '1' case 1 default null case false default '' case false default 0 case false default 'a' case 'a' kol 'a'
KN35. '1' dizgisi switch’te case 1’e gitti, match’te ise 1 => koluna uymayıp default’a düştü. null, boş dizgi ve 0 switch’te case false’e gitti, çünkü gevşek eşitlikte false’la eşittirler; match’te hiçbiri false => koluna uymadı. Yalnız 'a' iki deyimde aynı yere gitti. Bu, İşleçler dersindeki eşitlik tablosunun denetim akışındaki yansımasıdır: switch bildirim üretmeden gevşek eşitliğin bütün sürprizlerini devralır.
İç İçe Döngülerde Çıkış
break bir döngüyü bitirir, continue bir sonraki tura geçer ve ikisi de bir sayı alabilir: break 2 iki iç içe döngüden birden çıkar, continue 2 dıştaki döngünün sonraki turuna geçer. Dil bu sayıyı geçerli bir düzey sınırı dışında kabul etmez, ama sayının neyi atlattığı programcının okuması gereken bir ayrıntıdır. Aşağıdaki dosya üç iç içe durumu çalıştırır ve döngü değişkenlerinin döngü bittikten sonraki değerini de basar.
<?php // cikis.php — break 2 ve continue 2 dis donguyu etkiler $satirlar = []; for ($i = 1; $i <= 3; $i++) { for ($j = 1; $j <= 3; $j++) { if ($j === 2) { continue; } if ($i === 2) { continue 2; } if ($i === 3 && $j === 3) { break 2; } $satirlar[] = $i . $j; } } echo implode(',', $satirlar), "\n"; echo "i: $i j: $j\n";
11,13,31 i: 3 j: 3
KN36. Çıktıdaki üç çift, her çıkışın etkisini gösterir: j ikiye eşit olduğunda iç tur atlandı, i ikiye eşit olduğunda dış turun geri kalanı atlandı, i üç ve j üç olduğunda iki döngü birden bitti. Son satır, PHP’de bir döngü değişkeninin döngü bittikten sonra da yaşadığını gösterir: i ve j hâlâ tanımlıdır ve çıkış anındaki değerlerini korur. Döngü değişkeninin kapsamı döngü değil, onu içeren fonksiyondur; bu, aynı adı sonradan kullanan koda kalıntı bırakabilir.
foreach ve Referans Tuzağı
foreach bir dizinin elemanlarını sırayla bir değişkene kopyalar; kopya değiştirilirse dizi etkilenmez. Değişken & ile bir referans olarak bağlanırsa dizi değişir ve döngü bittikten sonra bu referans yaşamaya devam eder. Referansı temizlemeden aynı adı yeniden kullanan ikinci bir foreach, dizinin son elemanının üzerine yazar. Aşağıdaki dosya üç durumu ölçer.
<?php // referans.php — foreach referansi donguden sonra da yasar $a = [1, 2, 3]; foreach ($a as $v) { $v *= 10; } echo 'kopya: ', implode(',', $a), "\n"; $b = [1, 2, 3]; foreach ($b as &$w) { $w *= 10; } foreach ($b as $w) { } echo 'referans: ', implode(',', $b), "\n"; $c = [1, 2, 3]; foreach ($c as &$x) { $x *= 10; } unset($x); foreach ($c as $x) { } echo 'unset ile: ', implode(',', $c), "\n";
kopya: 1,2,3 referans: 10,20,20 unset ile: 10,20,30
KN37. Kopya ile dönen foreach diziyi değiştirmedi. Referansla dönen, her elemanı on katına çıkardı. Ardından aynı adı bir kopya olarak kullanan ikinci döngü, referans hâlâ son elemana bağlı olduğu için dizinin son elemanını sessizce ezdi ve 10,20,20 çıktı. unset($x) referansı koparttı ve 10,20,30 kaldı. Bu davranış sessizdir: bildirim yok, istisna yok, yalnız yanlış bir dizi.
Koşulun İçinde Atama
Tek bir karakter farkı, bir koşulu bir atamaya çevirir: = ile ==. PHP bunu sözdizim hatası saymaz, çünkü atama bir ifadedir ve değeri atanan değerdir. Koşul o değere göre karar verir. Aşağıdaki dosya aynı koşulu üç biçimde yazar; hata işleyicisi bir sayaç tutar.
<?php // kosul_atama.php — if icinde = atama yapar ve degeri kosul sayilir $bildirim = 0; set_error_handler(function (int $seviye) use (&$bildirim): bool { $bildirim++; return true; }); $durum = 'bos'; if ($durum = 'dolu') { echo "atama: dal dogru, durum: $durum\n"; } $durum = 'bos'; if ($durum == 'dolu') { echo "karsilastirma: dal dogru\n"; } else { echo "karsilastirma: dal yanlis, durum: $durum\n"; } $deger = 5; if ($deger = 0) { echo "sifir atama: dal dogru\n"; } else { echo "sifir atama: dal yanlis, deger: $deger\n"; } echo 'bildirim: ', $bildirim, "\n";
atama: dal dogru, durum: dolu karsilastirma: dal yanlis, durum: bos sifir atama: dal yanlis, deger: 0 bildirim: 0
KN38. 'dolu' atandığında koşul doğru çıktı ve $durum değişti; karşılaştırma ise değeri korudu. 0 atanınca koşul yanlış çıktı ve $deger beşten sıfıra düştü. Üç durumda da sayaç sıfır kaldı: dil atamayı koşul içinde meşru sayar ve bir sinyal vermez. Hata, yalnız değişkenin değerinin koşuldan sonra değişmiş olmasından anlaşılır.
Döngü Kaç Kez Döner
Bir döngünün kaç tur attığı koşuldan çıkarılabilir, ama üç durumda beklenti ile gerçek ayrışır. do while koşulu turdan sonra sınar, bu yüzden yanlış bir koşulda bile bir kez döner. Kayan noktalı bir sayaç, 0.1 adımlarla 1’e ulaştığında beklenenden bir tur fazla atabilir. Döngü sırasında diziye eleman eklemek, foreach’in dönüş sayısını değiştirmez, çünkü foreach başlangıç anındaki diziyi dolaşır. Aşağıdaki dosya üçünü sayar.
<?php // tur_sayisi.php — do while bir kez doner, kayan nokta sayaci fazla doner $say = 0; do { $say++; } while (false); echo "do while: $say\n"; $say = 0; for ($x = 0; $x < 1; $x += 0.1) { $say++; } echo "for 0.1 adim: $say\n"; $dizi = [1, 2]; $say = 0; foreach ($dizi as $eleman) { $dizi[] = $eleman * 10; $say++; } echo "foreach: $say tur, dizi: ", count($dizi), " eleman\n";
do while: 1 for 0.1 adim: 11 foreach: 2 tur, dizi: 4 eleman
KN39. do while yanlış koşulda bile bir tur attı. Kayan noktalı sayaç on değil on bir tur attı, çünkü onuncu toplamda sayaç tam 1 olmadı, 1’in hemen altında kaldı; bu, Değişkenler dersindeki 0.1 + 0.2 ölçümünün döngü tarafıdır. foreach iki tur attı ve dizi dört elemana çıktı: dönülen dizi ilk andaki kopyadır. Üçü de sessizdir.
foreach ve Dizi Olmayan Değer
foreach bir dizi ya da bir nesne bekler. Başka bir değer verilirse döngü hiç dönmez ve dil bir E_WARNING bildirir; yani bu durum, bu dersin ölçü sınıfından farklı olarak, durdurmaz ama sessiz de değildir. Aşağıdaki dosya null, bir dizgi ve boş bir diziyi aynı döngüye verir.
<?php // dizi_degil.php — foreach dizi olmayan degerde uyarir ve donmez set_error_handler(function (int $seviye): bool { echo $seviye === E_WARNING ? " [E_WARNING]\n" : " [baska]\n"; return true; }); $girdiler = [ 'null' => null, "'abc'" => 'abc', '[]' => [], '[1, 2]' => [1, 2], ]; foreach ($girdiler as $ad => $girdi) { $say = 0; foreach ($girdi as $eleman) { $say++; } echo "$ad: $say tur\n"; }
[E_WARNING] null: 0 tur [E_WARNING] 'abc': 0 tur []: 0 tur [1, 2]: 2 tur
KN40. null ve dizgi döngüye girmedi ve her biri bir E_WARNING bildirdi; boş dizi ve iki elemanlı dizi bildirimsiz döndü. Boş dizi ile null arasındaki bu fark, bir fonksiyonun “sonuç yok” için hangisini döndürdüğünü bir sözleşme hâline getirir: null döndürüp foreach’e vermek her çağrıda bir uyarı üretir.
Döngü Sırasında Değişen Dizi
foreach değerle çalışırken diziyi başlangıçtaki hâliyle dolaşır; döngü içinde dizi büyütülse bile tur sayısı değişmez. for ise koşulu her turda yeniden hesaplar; koşul count() ise dizi büyüdükçe tur da uzar. Aşağıdaki dosya aynı büyütmeyi iki döngüde sayar.
<?php // buyume.php — foreach baslangic dizisini dolasir, for kosulu her turda yeniden hesaplar $a = [1, 2, 3]; $tur_a = 0; foreach ($a as $v) { if ($tur_a < 5) { $a[] = $v; } $tur_a++; } echo 'foreach: ', $tur_a, ' tur, ', count($a), " eleman\n"; $b = [1, 2, 3]; $tur_b = 0; for ($i = 0; $i < count($b); $i++) { if ($tur_b < 3) { $b[] = 9; } $tur_b++; } echo 'for: ', $tur_b, ' tur, ', count($b), " eleman\n";
foreach: 3 tur, 6 eleman for: 6 tur, 6 eleman
KN41. foreach üç tur döndü ve dizi altı elemana çıktı: dolaşılan, başlangıçtaki üç elemanlı kopyaydı. for altı tur döndü, çünkü count($b) her turda yeni uzunluğu verdi. Biri büyümeyi görmez, öbürü görür; ikisi de bildirim üretmez. Döngünün döndüğü dizi döngü içinde değişiyorsa tur sayısı, hangi döngü yapısının seçildiğine bağlıdır.
Sınırlayıcı Ölçüm
match karşılıksız girdide durdurduğu için, bir kolun default taşıması durdurmayı sessize çevirir. Bu ders sınıfının sınırı budur: aynı deyim, tek bir kol eklenince durdurur sınıfından sessiz sınıfa geçer. Aşağıdaki dosya, default kolu olan ve olmayan iki fonksiyonu aynı girdilerle çalıştırır. Sonuç önce hesaplanır, sonra yazdırılır.
<?php // varsayilan.php — default kolu match'in durdurmasini sessize cevirir function ad_katisi(int $kod): string { return match ($kod) { 1 => 'bir', 2 => 'iki', }; } function ad_varsayilan(int $kod): string { return match ($kod) { 1 => 'bir', 2 => 'iki', default => 'bilinmiyor', }; } foreach ([1, 7] as $kod) { try { $katisi = ad_katisi($kod); } catch (UnhandledMatchError $hata) { $katisi = get_class($hata); } $varsayilan = ad_varsayilan($kod); printf("%d katisi: %-20s varsayilan: %s\n", $kod, $katisi, $varsayilan); }
1 katisi: bir varsayilan: bir 7 katisi: UnhandledMatchError varsayilan: bilinmiyor
KN42. 1 girdisinde iki fonksiyon de 'bir' döndürdü. 7 girdisinde default kolu olmayan fonksiyon UnhandledMatchError fırlattı, olan fonksiyon ise 'bilinmiyor' döndürdü ve hiçbir şey bildirmedi. Bu durumda default iki şeyi birden yapar: eşleşmeyen girdiyi sessizce kabul eder ve girdinin eşleşmediğini programdan saklar. Hangi seçimin doğru olduğu, eşleşmeyen girdinin beklenen mi yoksa bir hata mı olduğuna bağlıdır; ölçüm yalnız seçimin sonucunu gösterir.
Özet
- Denetim akışının ölçü sınıfı durdurur:
matchkarşılıksız koldaUnhandledMatchErrorfırlatır,switchise sessizce geçer ve değişkeni önceki değerinde bırakır. switchetiketleri gevşek,matchkolları katı eşitlikle karşılaştırır:'1'switch’tecase 1’e gider,match’tedefault’a düşer.break 2vecontinue 2iç içe döngülerde dış döngüyü etkiler; döngü değişkenleri döngüden sonra da tanımlı kalır.- Referansla çalışan bir
foreachdöngüden sonra referansı bırakır;unsetolmadan aynı adla ikinci bir döngü dizinin son elemanını ezer. match’e birdefaultkolu eklemek durdurmayı sessize çevirir ve eşleşmeyen girdiyi gizler.
Sonraki Adım
Karşılıksız bir dalın ne yaptığı görüldü: switch geçer, match durdurur. Fonksiyonlar dersi aynı soruyu çağrılara soruyor: bir fonksiyona beklenenden fazla argüman verildiğinde, fazlalık nereye gidiyor?
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.