for, range() ve döngü problem kalıpları
Geçen hafta while ile koşul doğru olduğu sürece tekrar yaptık. Bazen de kaç tekrar yapacağımız baştan bellidir: 1’den 10’a kadar sayıları işlemek, 7 günlük veri girmek veya 6 taksitlik bir ödeme planı oluşturmak gibi.
Bu tür durumları Python’da for döngüsü ve range() fonksiyonuyla daha kısa yazabiliriz. Bu hafta for söz dizimini öğrendikten sonra sayma, biriktirme, arama ve özetleme gibi birçok problemde karşımıza çıkan döngü kalıplarını tanıyacağız.
Bu bölümün kapsamı
range()nesnesinin ürettiği değerler ve listeden farkırange()biçimleri ve bitiş değerinin dışarıda kalması- Sayma ve biriktirme kalıpları
- Arama kalıbında
breakile erken bitirme continueile yalnızca o yinelemeyi atlamawhile Truevebreakile geçerli girdi kalıbı ve bu kalıbın özel bitirme değerinden (sentinel) farkı- Probleme göre
forilewhilearasında seçim
enumerate()vezip()kullanımı- İkiden çok katmanlı iç içe döngüler
Bu başlıklar konunun devamıdır; ileride karşınıza çıkar ama bu derste ezberlemeniz beklenmiyor.
range(1, 5) neden 5 değerini üretmiyor?
Cevap tek bir kurala dayanır: range() bitiş değerini üretmez. Döngü sınırlarında yapılan hataların çoğu bu kuralı unutmaktan çıkar. Bölümün ikinci yarısında döngüyle çözülen problemleri türlerine ayıracağız: saymak, biriktirmek, aramak ve döngüden erken çıkmak ayrı kalıplardır.
for ile belirli sayıda tekrar
Aşağıdaki program 0, 1, 2, 3 ve 4 değerlerini yazdırır:
for sayi in range(5):
print(sayi)range(5) döngü sırasında şu değerleri üretir:
0, 1, 2, 3, 4
Bitiş değeri olan 5 dâhil değildir.
Bu davranış başta alışılmadık gelebilir, ama kaç değer üretileceğini hesaplamayı kolaylaştırır: range(5) beş değer, range(2, 6) ise 6 - 2 = 4 değer üretir.
range() bir liste değildir
range(5) ile döngüde 0–4 değerlerini dolaşabilsek de range() çağrısı bir liste oluşturmaz. Python bunun yerine ayrı bir range nesnesi oluşturur. Nesne (object), Python’ın bellekte tuttuğu, türü belli bir değerdir; range nesnesi de değerleri döngüye sırası gelince verir:
print(range(5))Çıktı:
range(0, 5)
Üretilen değerleri bir kerede görmek isterseniz list() kullanabilirsiniz:
print(list(range(5)))Çıktı:
[0, 1, 2, 3, 4]
for sayi in range(5): yazarken list(range(5)) yapmanız gerekmez. Döngü değerleri range nesnesinden doğrudan sırayla alır. Burada list() yalnızca aralığın içeriğini göstermek için kullanıldı.
Tahmin et: range() hangi değerleri üretir?
Aşağıdaki kodu çalıştırmadan önce çıktıyı tahmin edin:
range(2, 9, 2):
2değerinden başlar,- Her adımda
2artar, 9değerine gelmeden durur.
Bu nedenle çıktı 2, 4, 6, 8 olur.
range() biçimleri
Üç temel kullanım biçimi vardır:
| Yazım | Ürettiği değerler |
|---|---|
range(5) |
0’dan başla, 5 hariç ilerle |
range(2, 6) |
2’den başla, 6 hariç ilerle |
range(10, 0, -2) |
10’dan başla, 0’a gelmeden ikişer azal |
Adım negatif olduğunda kural değişmez; 0 için de ayrı bir kural yoktur. Bitiş (stop) değeri hiçbir durumda dâhil edilmez. Değişen yalnızca ilerleme yönüdür.
Örneğin:
print(list(range(5, 0, -1)))şunu verir:
[5, 4, 3, 2, 1]
Listede 0 yok, çünkü bu çağrıda bitiş değeri 0’dır. Başlangıcın dâhil, bitişin hariç olduğu bu tür aralığa yarı açık aralık denir. Adım pozitif olduğunda da aynı kural geçerlidir.
for sayi in range(5, 0, -1):
print(sayi)Çıktı:
5
4
3
2
1
Alıştırma: son değeri dâhil edin
1’den 10’a kadar on sayının hepsini yazdırmak istiyoruz. Aşağıdaki kod 10 değerini atlıyor.
range() çağrısındaki bitiş değerini düzeltin.
range(1, 11) ifadesini deneyin.
for sayi in range(1, 11):
print(sayi)Sayaç ve biriktirici
Döngülerde iki değişken rolü sık görülür:
- Sayaç: kaç kez işlem yapıldığını ya da kaçıncı adımda olduğumuzu tutar,
- Biriktirici: toplam, çarpım gibi her turda güncellenen sonucu tutar.
Örneğin 1’den 5’e kadar sayıların toplamı:
toplam = 0
for sayi in range(1, 6):
toplam = toplam + sayi
print(toplam)Burada sayi, range() çağrısından sırayla gelen değerdir; toplam ise biriktiricidir.
İzleme tablosu:
sayi |
Yeni toplam |
|---|---|
| 1 | 1 |
| 2 | 3 |
| 3 | 6 |
| 4 | 10 |
| 5 | 15 |
Alıştırma: haftalık çalışma süresini toplayın
Bir öğrencinin her gün sabit 45 dakika çalıştığını ve bunu 7 gün sürdürdüğünü varsayalım. Toplam çalışma süresini biriktiriciyle hesaplayın.
toplam_dakika = toplam_dakika + 45 satırı her yinelemede çalışmalıdır.
toplam_dakika = 0
for gun in range(1, 8):
toplam_dakika = toplam_dakika + 45
print("Toplam dakika:", toplam_dakika)Sayma kalıbı
Bazen değerlerin toplamını değil, bir koşulu kaç değerin sağladığını bulmak isteriz.
Örneğin 1–20 arasında 3 ile tam bölünen kaç sayı olduğunu bulalım:
adet = 0
for sayi in range(1, 21):
if sayi % 3 == 0:
adet = adet + 1
print(adet)Buradaki sayaç her yinelemede değil, yalnızca koşul doğru olduğunda artar.
Tahmin et ve çalıştır
Aşağıdaki kod 1–15 arasında kaç çift sayı olduğunu sayıyor. Çalıştırmadan önce sonucu tahmin edin:
Sonuç 7 olmalıdır: 2, 4, 6, 8, 10, 12, 14.
Arama kalıbı ve break
Bir aralıkta aradığımız ilk değeri bulduktan sonra döngünün devam etmesine gerek kalmayabilir. break, içinde bulunduğu döngüyü hemen sonlandırır.
Örneğin 50’den büyük ve 7 ile tam bölünen ilk sayıyı bulalım:
for sayi in range(51, 100):
if sayi % 7 == 0:
print("Bulundu:", sayi)
breakİlk uygun değer 56 olduğunda döngü sona erer.
break çalıştığında döngü, koşulu yanlış olduğu için değil, programcı o noktada bitirmek istediği için biter. Gereksiz yere kullanılan break kodun akışını izlemeyi zorlaştırabilir. Aranan sonuç bulunduktan sonra kalan turlar bir işe yaramıyorsa break kullanmak uygundur.
Alıştırma: ilk uygun katı bulun
30’dan büyük, 8 ile tam bölünen ilk sayıyı bulun. Aşağıdaki program uygun değerlerin hepsini yazdırıyor; yalnızca ilkini yazdıracak biçimde değiştirin.
print() satırından sonra aynı girinti düzeyinde break yazın.
for sayi in range(31, 80):
if sayi % 8 == 0:
print("Bulundu:", sayi)
breakGeçerli girdi gelene kadar: while True + break
Geçen hafta input() satırını döngüden önce ve döngü içinde iki kez yazdığımız örneği gördük. Artık break’i bildiğimize göre aynı akışı tek bir input() satırıyla yazabiliriz:
while True:
puan = int(input("1-5 arasında puan: "))
if 1 <= puan <= 5:
break
print("Geçersiz değer")
print("Kabul edilen puan:", puan)while True koşulu hiçbir zaman yanlış olmaz. Bu yüzden bu kalıpta çıkış koşulunu ve break satırını sizin yazmanız gerekir. Geçerli değer gelince break döngüyü sonlandırır; değer geçersizse yeni bir yineleme başlar ve puan yeniden sorulur.
Bu örnek de kullanıcının int() ile sayıya çevrilebilecek bir değer yazdığını varsayar. Sayıya çevrilemeyen metinle ne yapılacağını 13. haftada, istisnalar konusunda öğreneceğiz.
continue: bu yinelemenin kalanını atla
continue döngüyü bitirmez. O anki yinelemenin geri kalanını atlar ve bir sonraki yinelemeye geçer.
Tek ve basit bir koşul için çoğu zaman continue gerekmez. Örneğin yalnızca saat == 3 durumunu atlamak istiyorsanız if saat != 3: yazmanız yeterlidir. continue en çok, veriyi işlemeden önce birden fazla geçersiz durumu ayıklamak gerektiğinde işe yarar; ana işlem satırları daha sade kalır.
Örneğin ölçüm listemizde -50 derecenin altındaki ve 60 derecenin üzerindeki değerleri geçersiz kabul edelim:
olcumler = [22, -80, 24, 75, 19]
toplam = 0
gecerli_adet = 0
for sicaklik in olcumler:
if sicaklik < -50:
continue
if sicaklik > 60:
continue
toplam = toplam + sicaklik
gecerli_adet = gecerli_adet + 1
print("Geçerli ölçüm:", gecerli_adet)
print("Toplam:", toplam)Bu örnekte iki eleme koşulu, toplama ve sayma satırlarından önce gelir. Geçersiz bir değerde continue çalışınca toplam ve sayaç güncellenmez; döngü sonraki ölçümle devam eder.
Aynı işi continue olmadan daha kısa ve açık bir koşulla yazabiliyorsanız onu seçin. Ama bir veriyi atlamak için birden fazla kontrol yapıyorsanız continue, asıl işi yapan satırları daha az girintili ve daha okunur tutabilir.
Özel bitirme değeri (sentinel)
Kaç veri geleceği baştan belli değilse for range(...) işe yaramayabilir. Kullanıcı veri girişini özel bir değer yazarak bitiriyorsa geçen haftaki while yapısını kullanırız.
Örneğin harcama tutarları girilsin; -1 girildiğinde giriş bitsin:
toplam = 0
tutar = float(input("Tutar (-1 bitirir): "))
while tutar != -1:
toplam = toplam + tutar
tutar = float(input("Tutar (-1 bitirir): "))
print("Toplam:", toplam)Burada -1 bir harcama değildir; yalnızca girişin bittiğini bildirir. Bu tür değere özel bitirme değeri (sentinel) denir.
Gerçek bir programda girilen tutarın geçerli olup olmadığı ayrıca kontrol edilmelidir. Bu örnekte -1 dışında negatif değer girilmediğini ve kullanıcının boş giriş yapmadığını varsayıyoruz. Kullanıcı hiçbir şey yazmadan Enter’a basarsa float("") dönüşümü ValueError hatası verir. Bu tür dönüşüm hatalarını 13. haftada try/except ile ele alacağız.
Hangi döngüyü seçmeliyim?
Basit bir başlangıç kuralı:
| Durum | Genellikle uygun yapı |
|---|---|
| Tekrar sayısı baştan belli | for + range() |
| Bir koşul sağlandığı sürece devam | while |
| Özel bitirme değeri gelene kadar devam | while |
| Belirli sayı aralığındaki değerleri işle | for + range() |
Tabloyu kesin bir kural gibi okumayın: aynı problem çoğu zaman iki yapıyla da çözülebilir. Seçerken kodun niyetini en açık anlatan yapıyı tercih edin.
Değiştir: taksit planını inceleyin
12000 TL’lik bir ürün 6 eşit taksitle ödenecek olsun:
Şunları deneyin:
taksit_sayisideğerini4yapın.- İlk ve son ayın
aydeğerini tahmin edin. range(1, taksit_sayisi)yazarsanız son taksitte ne olur?- Her ayın başındaki ve sonundaki
kalandeğerini küçük bir izleme tablosuna yazın.
Üret: aylık taksit borcunu azaltın
Bir cihaz 9000 TL borçla alınmış, her ay 1500 TL ödenecek. 6 ay boyunca her ödemeden sonra kalan borcu yazdıran bir program yazın.
Başlangıç kodu:
Kabul kriterleri:
forverange()kullanılmalı,- Aylar
1’den6’ya kadar işlenmeli, - Her ay
kalan_borcdeğerindenaylik_odemeçıkarılmalı, - Ay numarası ve kalan borç yazdırılmalı,
- Son ayın sonunda kalan borç
0olmalı.
for ay in range(1, 7): ile başlayabilirsiniz.
kalan_borc = 9000
aylik_odeme = 1500
for ay in range(1, 7):
kalan_borc = kalan_borc - aylik_odeme
print("Ay:", ay, "Kalan borç:", kalan_borc)İç içe döngü ne zaman gerekir?
Bir tekrarın içinde başka bir tekrar gerekiyorsa döngüleri iç içe yazarız. Bu derste iç içe döngüyü yalnızca problemde gerçekten iki ayrı tekrar olduğunda kullanacağız: katlar ve her kattaki odalar gibi.
Örneğin iki katlı ve her katta üç odası olan bir binanın oda etiketlerini yazdıralım:
for kat in range(1, 3):
for oda in range(1, 4):
print("Kat:", kat, "Oda:", oda)Dış döngünün her yinelemesinde iç döngü baştan sona çalışır. Ekrana toplam 2 × 3 = 6 satır yazılır.
Bu hafta yıldızlarla üçgen çizmek gibi karmaşık desenlere girmeyeceğiz. Aklınızda kalması gereken şu: problemde birbirinden bağımsız iki tekrar varsa iç içe döngü gerekebilir.
Tek sayfa özet
Bu hafta belirli sayıda tekrarı for ve range() ile yazdık ve sık kullanılan döngü kalıplarını gördük.
range()bir liste değil, aralıktaki değerleri sırayla veren ayrı birrangenesnesidir; değerleri görmek içinlist(range(...))kullanılabilir.range()bitiş değerini, adım pozitif de olsa negatif de olsa dâhil etmez.range(baslangic, son, adim)ile başlangıç ve ilerleme miktarı belirlenebilir.- Sayaç bir şeyin kaç kez olduğunu, biriktirici ise toplam gibi biriken sonucu tutar.
- Koşulu sağlayan değerleri saymak için sayaç yalnızca
ifdoğru olduğunda artırılabilir. breakaranan sonuç bulunduğunda veya geçerli girdi alındığında döngüyü tamamen bitirebilir.while True+breakile geçerli girdi gelene kadar sorma kalıbındainput()satırını iki kez yazmak gerekmez.continueyalnızca o anki yinelemenin kalanını atlar; özellikle geçersiz veriyi birden fazla kontrolle ayıklarken işe yarar.- Tekrar sayısı baştan biliniyorsa
for, koşula bağlı belirsiz tekrar içinwhileçoğu zaman daha açıklayıcıdır. - Özel bitirme değeri (
sentinel) veri girişinin sona erdiğini bildirmek için kullanılabilir; boş ya da sayıya çevrilemeyen girdi ise ayrıca ele alınmalıdır. - İç içe döngü, problemde gerçekten iki tekrar boyutu olduğunda kullanılmalıdır.
Bir sonraki bölümde tekrar eden kod parçalarını fonksiyonlara ayıracak; parametreler ve dönüş değerleriyle problemi daha küçük parçalara böleceğiz.
Bu bölümün kazanımları
Bu bölümü bitiren öğrenci:
range()nesnesinin ürettiği değerleri belirler ve listeden farkını açıklar.range()çağrısının farklı biçimlerini kullanır ve bitiş değerinin dışarıda kaldığını hesaba katar.- Sayma ve biriktirme kalıplarını
forile kurar. - Arama kalıbında
breakile döngüyü erken bitirir. continueile yalnızca o yinelemenin kalanını atlar.while Truevebreakile geçerli girdi gelene kadar soran döngüyü yazar ve bu kalıbı sentinel kullanımından ayırır.- Problemin yapısına göre
forilewhilearasında seçim yapar.