İçeriğe geç
academia.sh

Ders 08 / 18

Döngü Denetimi

Erken çıkış, turu atlama, arama kalıbı ve iç içe döngülerden çıkışta okunabilirlik.

İçindekiler

Önceki ders döngüleri baştan sona akan yapılar olarak tanıttı: koşul sınanır, gövde çalışır, tekrar edilir. Bazı problemler bu düzeni bozmayı gerektirir. Aranan değer bulunduğunda kalan elemanlara bakmak gereksizdir; geçersiz bir eleman ise işlenmeden atlanmalıdır.

Bu ders, döngü akışını içeriden yönlendiren deyimleri ve bunların okunabilirlik üzerindeki etkisini ele alır.

Erken Çıkış

Erken çıkış deyimi, döngüyü koşulu beklemeden sonlandırır. Akış, döngünün hemen altından devam eder.

olcumler = [12, 18, 7, 25, 14]
SINIR = 20

for olcum in olcumler:
    if olcum > SINIR:
        print("sınırı aşan ilk ölçüm:", olcum)   # 25
        break

Erken çıkışın gerekçesi yalnızca hız değildir. break olmadan yazılan bu döngü, sınırı aşan son ölçümü bulurdu; aranan ilk ölçüm olduğunda çıkış anlamın parçasıdır.

Sonsuz döngüler de çıkışını buradan alır: koşul her zaman doğru yazılır, gövdenin içinde bir noktada çıkış kararı verilir. Bu yazım, çıkış koşulu turun ortasında hesaplanıyorsa tercih edilir.

Turu Atlama

İkinci deyim, mevcut turu erkenden bitirir ve bir sonrakine geçer. Döngü sonlanmaz.

ham = [12, -1, 18, -1, 25]

toplam = 0
sayilan = 0
for olcum in ham:
    if olcum < 0:            # geçersiz ölçüm işareti
        continue
    toplam += olcum
    sayilan += 1

print(toplam, sayilan)       # 55 3

Aynı iş, gövdeyi bir koşul bloğuna alarak da yazılabilir. İki yazım arasındaki seçim girinti derinliğine bakılarak yapılır: atlama deyimi, geçersiz durumları başta eleyip gövdenin geri kalanını düz tutar. Gövde uzunsa bu belirgin bir kazançtır.

Arama Kalıbı ve Bulunamama Durumu

Erken çıkışın en yaygın kullanımı aramadır. Aramanın gizli sorusu her zaman aynıdır: aranan bulunamazsa ne olur?

def ilk_asani_bul(olcumler: list[int], sinir: int) -> int | None:
    """Sınırı aşan ilk ölçümü döndürür; yoksa None döndürür."""
    for olcum in olcumler:
        if olcum > sinir:
            return olcum
    return None              # döngü hiç çıkmadan bittiyse: bulunamadı

print(ilk_asani_bul([12, 18, 7, 25, 14], 20))    # 25
print(ilk_asani_bul([12, 18, 7], 20))            # None

Son satırdaki return None, döngünün tükendiği durumu ele alır. Bu satır unutulursa fonksiyon sessizce boş değer döndürür ve çağıran taraf, aramanın başarısız olduğunu anlamak yerine beklenmedik bir değerle karşılaşır.

Bulunamama durumunu bildirmenin üç yolu vardır ve seçim, çağıranın ne yapacağına bağlıdır: özel bir değer döndürmek, bir hata fırlatmak veya “bulundu mu” bilgisini sonuçla birlikte döndürmek. İlk seçenek en yaygın olanıdır; ancak dönen değerin denetlenmesi zorunludur, aksi hâlde temel veri tipleri dersinde tanımlanan boş değer sorunu ortaya çıkar.

Bazı diller, döngü hiç erken çıkmadan bittiğinde çalışan ayrı bir blok sunar; aynı işi bayrak değişkeni olmadan yazmayı sağlar. Bu yapı evrensel değildir.

Bayrak Değişkeni

Erken çıkışın bulunmadığı veya uygun olmadığı durumlarda, sonucu bir mantıksal değişkende taşımak yaygın bir kalıptır:

bulundu = False
for olcum in [12, 18, 7]:
    if olcum > 20:
        bulundu = True
        break

print(bulundu)               # False

Bayrak, döngü bittikten sonra hangi yoldan çıkıldığını söyler. Kalıp doğrudur ancak maliyeti okunabilirliktir: okuyucu, bayrağın nerede kurulduğunu ve nerede okunduğunu izlemek zorundadır. Arama bir fonksiyona ayrılabiliyorsa, doğrudan dönüş çoğu zaman daha açıktır.

İç İçe Döngülerden Çıkış

Erken çıkış deyimi yalnızca kendi döngüsünü sonlandırır. İki düzeyli bir aramada iç döngüden çıkmak, dış döngüyü durdurmaz.

matris = [[1, 2, 3],
          [4, 5, 6],
          [7, 8, 9]]

def konum_bul(matris: list[list[int]], hedef: int) -> tuple[int, int] | None:
    """Hedefin (satır, sütun) konumunu döndürür; yoksa None."""
    for i, satir in enumerate(matris):
        for j, deger in enumerate(satir):
            if deger == hedef:
                return (i, j)      # her iki döngüden birden çıkar
    return None

print(konum_bul(matris, 6))        # (1, 2)
print(konum_bul(matris, 99))       # None

Üç seçenek vardır. Fonksiyona ayırıp dönüş kullanmak, yukarıdaki gibi, en okunur olanıdır: tek bir deyim tüm düzeylerden çıkar. Bayrak değişkeni kullanmak, her düzeyde ayrı bir çıkış koşulu gerektirir ve derinlik arttıkça hızla okunmaz hâle gelir. Bazı diller etiketli çıkış sunar; hangi döngüden çıkılacağı açıkça yazılır.

Bu, fonksiyonların yalnızca tekrar kullanım için değil, akışı sadeleştirmek için de kullanıldığının ilk örneğidir. Sonraki konu bu aracı ayrıntılı ele alacaktır.

Döngü Değişkenini Değiştirmek

Bir uyarı: sayaçlı döngünün değişkenini gövde içinde değiştirmek, birçok dilde beklenen etkiyi yapmaz veya sonlanma güvencesini bozar. Aynı biçimde, üzerinde yineleme yapılan bir koleksiyonu döngü sürerken değiştirmek — eleman eklemek veya silmek — tanımsız ya da şaşırtıcı davranışa yol açar.

Güvenli kalıp, değişiklikleri ayrı bir koleksiyonda toplamak ve döngü bittikten sonra uygulamaktır:

olcumler = [12, -1, 18, -1, 25]

gecerli = [olcum for olcum in olcumler if olcum >= 0]   # yeni liste üretilir
print(gecerli)               # [12, 18, 25]
print(olcumler)              # [12, -1, 18, -1, 25]  — özgün liste değişmedi

Son örnekteki yazım, bir döngünün kısaltılmış biçimidir ve birçok dilde karşılığı vardır. Süzme ve dönüştürme işlemlerini tek satıra indirir; koşul karmaşıklaştığında açık döngü daha okunur kalır.

Erken Çıkış ve Tembel Yineleme

Erken çıkışın kazancı, işlenmeyen elemanların sayısıyla orantılıdır. Koleksiyon zaten bellekte duruyorsa kazanç yalnızca zamandır. Ancak elemanlar istendikçe üretiliyorsa, erken çıkış hiç üretilmeyen elemanların maliyetini de ortadan kaldırır.

Bu üretim biçimine tembel yineleme denir: değerler önceden hesaplanıp bir listede toplanmaz, döngü her tur istedikçe hesaplanır.

def olcum_kaynagi():
    """Değerleri istendikçe üretir; hepsi birden bellekte tutulmaz."""
    for taban in range(1, 1_000_000):
        yield taban * 3

for deger in olcum_kaynagi():
    if deger > 20:
        print("ilk aşan:", deger)     # ilk aşan: 21
        break

Döngü yedinci turda çıkar; geri kalan değerler hiç hesaplanmaz. Aynı iş, önce bir milyon elemanlık liste kurup sonra aramakla da yapılabilirdi — sonuç aynı, maliyet karşılaştırılamaz.

Ayrım büyük veri kaynaklarında belirleyicidir: bir dosyanın tamamını belleğe okumak ile satır satır işlemek arasındaki fark, aynı fikrin uygulamasıdır.

Özet

  • Erken çıkış döngüyü koşulu beklemeden sonlandırır; ilk eşleşmeyi arayan problemlerde anlamın parçasıdır.
  • Turu atlama deyimi yalnızca mevcut turu bitirir ve geçersiz durumları başta eleyerek girinti derinliğini azaltır.
  • Arama kalıbında bulunamama durumu açıkça ele alınır; dönen özel değer çağıran tarafından denetlenmelidir.
  • Bayrak değişkeni erken çıkışın alternatifidir, ancak okuyucuya ek izleme yükü getirir.
  • Erken çıkış yalnızca kendi döngüsünü sonlandırır; iç içe döngülerde en okunur çözüm, aramayı fonksiyona ayırıp dönüş kullanmaktır.
  • Üzerinde yineleme yapılan koleksiyonu döngü sürerken değiştirmek güvenli değildir.

Sonraki Adım

Buraya kadar denetim yapıları doğrudan koda yazıldı. Karmaşık bir problemde ise önce çözümün kendisi tasarlanır, sonra koda çevrilir. Sonraki ders, dilden bağımsız çözüm yazımını — sözde kodu — ve bir çözümün koda dönüştürülmeden önce nasıl sınanacağını ele alacak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat