Koşullar, döngüler ve string işlemleri
C’de kullandığınız if yapısını ve döngüleri bu hafta Python’da yazacağız. Bu kavramları baştan anlatmayacağız, Python’da nerede farklı yazıldıklarına bakacağız. Python’da bir listenin ya da bir metnin elemanlarını sayaç tutmadan sırayla dolaşabilirsiniz. Bölümün ikinci yarısında metinlerle, yani string’lerle çalışacağız: bir metin satırını parçalara ayıracak, parçaları yeniden birleştireceğiz. Python’da metnin türü str’dir.
Bu sayfadaki kod hücreleri kendiliğinden çalışmaz. Her hücrede önce çıktıyı tahmin edin, sonra Run Code düğmesine basın. Kodu değiştirip yeniden çalıştırabilir, Start Over ile ilk hâline döndürebilirsiniz.
Bu bölümün kapsamı
if/elif/elsezincirinde sınır değerinin hangi dala düştüğü- Bir listenin ya da metnin elemanları üzerinde doğrudan dolaşma;
enumerate()vezip() range()bitişinin dışarıda kalması ve adım parametresiwhiledöngüsünde koşuldaki değeri değiştiren adımbreakilecontinuefarkı- String’de indeksleme ve dilimleme:
text[1:5],text[::2],text[::-1] - String’in değiştirilemez olması
strip(),split()vejoin()ile bir metin kaydını parçalara ayırma ve birleştirme
lower()/upper()dönüşümünün TürkçeI/İsınırı- Unicode normalleştirme biçimleri ve yerel ayar (locale) kütüphaneleri
- Düzenli ifadeler (regular expressions)
- Liste kavraması (list comprehension)
Bu başlıklar konunun devamıdır. İleride karşınıza çıkar, ama bu derste ezberlemeniz beklenmiyor.
Bu C döngüsü Python’da nasıl yazılır?
Programlama Temelleri’nde buna benzer döngüler yazdınız. Program bir dizideki notları sırayla okuyor, her öğrencinin harf notunu yazıyor ve kaç öğrenciyi değerlendirdiğini sayıyor. Dizide 0 sınava girmeyen öğrenciyi, -1 ise verinin bittiğini gösteriyor:
#include <stdio.h>
int main(void) {
int scores[] = {85, 70, 0, 64, -1, 90};
int count = 0;
for (int i = 0; scores[i] != -1; i++) {
if (scores[i] == 0) {
continue; // sınava girmedi
}
if (scores[i] >= 85) {
printf("%d. öğrenci: AA\n", i + 1);
} else if (scores[i] >= 70) {
printf("%d. öğrenci: BB\n", i + 1);
} else {
printf("%d. öğrenci: kaldı\n", i + 1);
}
count++;
}
printf("Değerlendirilen: %d\n", count);
return 0;
}Bu program ne yazar? Üçüncü öğrenci için bir satır çıkar mı, 90 notuna sıra gelir mi? Önce tahmin edin, sonra çıktıyı açın.
1. öğrenci: AA
2. öğrenci: BB
4. öğrenci: kaldı
Değerlendirilen: 3
Bu programı Python’a çevirdiğinizi düşünün. i sayacı Python’da da gerekir mi? -1 kontrolü yine döngünün koşulunda mı durur? Hangi satırlar kısalır, hangileri başka bir yapıya dönüşür? Okumaya devam etmeden önce aklınıza gelenleri bir kenara yazın.
Bir fark daha var. Gerçek bir programda bu notlar bir dosyadan 85,70,0,64,-1,90 gibi tek bir metin satırı olarak gelir. C’de bu satırı sayılara ayırmak için strtok() ve atoi() gibi fonksiyonlarla uğraşırdınız. Örneği kısa tutmak için notları diziye biz yazdık. Python’da satırı parçalamak iki metotla biter.
Bölüm bu soruların cevabını sırayla veriyor. Sonunda programın Python hâlini birlikte okuyacağız, notları da metin satırından alacağız.
if: parantez ve süslü parantez yok
Aşağıdaki hücre bir notu harf notuna çeviriyor. Hücrede score değeri 72. Hangi sonuç yazılır?
Python’da koşulun sonuna : gelir, bloğu girinti belirler. else if yerine elif yazılır.
Zincirdeki koşullar yukarıdan aşağı sırayla sınanır. İlk doğru koşulun bloğu çalışır, zincirin geri kalanına hiç bakılmaz. Aynı koşulları ayrı ayrı if satırlarıyla yazsaydınız her if kendi başına sınanırdı ve birden çok blok çalışabilirdi.
Sınır değerini düşünün
score = 70 için sonuç ne olur? 69 için ne olur? Önce tahmin edin, sonra hücredeki değeri değiştirip yeniden çalıştırın. İkinci koşulda >= yerine > yazılsaydı 70 hangi dala düşerdi? Bunu da hücrede deneyebilirsiniz. Koşul zincirini sınarken sınır değerini ve iki yanındaki değerleri (69, 70, 71) ayrıca deneyin. Doğru görünen koddaki hatalar çoğu zaman bu değerlerde ortaya çıkar.
Liste: birden çok değeri sırayla tutmak
Bu bölümün örneklerinde köşeli parantez içinde yazılmış değerler göreceksiniz, [80, 95, 70] gibi. Bu bir listedir (list). Liste, birden çok değeri sırayla tutan bir Python değeridir ve C’deki diziye benzer. Elemanlara 0’dan başlayan indeksle ulaşırsınız, len() eleman sayısını verir. C dizisinden iki farkı var: boyutu baştan yazmazsınız ve append() metoduyla listenin sonuna yeni eleman ekleyebilirsiniz.
Hücrenin son iki satırı ne yazar?
Bu hafta listeyi iki yerde kullanacağız: for ile elemanlarını dolaşırken ve split() metodunun döndürdüğü değer olarak. Listenin öteki işlemlerini ve iki adın aynı listeyi gösterdiği durumları gelecek hafta göreceğiz.
for: sayaç tutmadan dolaşmak
C’de bir diziyi ya da metni dolaşırken bir indeks değişkeni tutardınız. Python’da for elemanları sırayla kendisi verir:
Bu kodda letter her turda sıradaki karakteri alır. İndeks gerekmediği için sayaç tutmuyoruz.
İndeks gerçekten gerekiyorsa range(len(word)) ile indeksleri dolaşıp her turda word[i] yazabilirsiniz. Hem indeks hem değer gerektiğinde ise enumerate() daha kısa bir yoldur. enumerate() her turda iki değer verir: sıra numarasını ve elemanı. İlk satırda sıra numarası 0 mı olur, 1 mi?
for index, letter in yazımı her turdaki iki değeri iki ayrı ada bağlar.
İki listeyi aynı anda dolaşmak için zip() kullanılır. zip() her turda iki listeden aynı sıradaki elemanları alıp eşler:
zip(), listelerden en kısası bitince durur ve hata vermez. Uzunlukların eşit olması gerekiyorsa bunu ayrıca kontrol etmelisiniz.
range() son değeri içermez
range(start, stop, step) en çok üç değer alır: başlangıç, bitiş ve adım. Tek değer verirseniz başlangıç 0, adım 1 olur. Geriye saymak için adım negatif verilir. Hücredeki üç satırın her biri hangi sayıları yazar? En çok üçüncü satır üzerinde düşünün:
Bitiş değeri hiçbir satırda listeye girmez, tıpkı C’deki for (i = 0; i < 5; i++) döngüsünde 5’in girmediği gibi. Geriye sayarken de bitiş dışarıda kalır. Hücredeki list() yalnız sayıları ekranda görmek için var, for içinde gerekmez.
Aşağıdaki döngü 10’dan geriye sayar. Son satırda hangi sayı yazar?
Adımın yönü başlangıçtan bitişe giden yönle uyuşmazsa aralık boş kalır: range(1, 5, -1) hiç sayı vermez. Emin değilseniz küçük bir list(range(...)) denemesiyle bakın.
while: koşula bağlı tekrar
Tekrar sayısı baştan belli değilse while kullanılır. Döngü, koşul doğru olduğu sürece döner. Hücre kaç satır yazar, son satırda kalan bakiye kaç olur?
while kullanırken üç soruyu sorun:
- Döngü başlarken koşuldaki değişkenlerin değeri ne?
- Her turda hangi değer değişiyor?
- Bu değişiklikle döngüyü bitirecek koşul gerçekten oluşabilir mi?
Bu sorulardan birinin cevabı belli değilse döngü hiç bitmeyebilir. Buna sonsuz döngü denir.
break ve continue
break, içinde bulunduğu en yakın döngüyü hemen bitirir. continue ise o turun kalanını atlayıp sonraki tura geçer. Hücre hangi sayıları yazdırır?
Bu örnekte -1, verinin bittiğini bildiren bir işaret değeri (sentinel) gibi döngüyü bitirir. C’de “-1 girilene kadar oku” diye yazdığınız döngüler bu kalıptır. continue satırı bu listede hiç çalıştı mı? -1’i listenin sonuna taşıyıp hücreyi yeniden çalıştırın. break ve continue’yu her yerde kullanmayın. Kodun amacını daha açık gösterdikleri yerde işe yararlar.
String bir karakter dizisidir
Bir string’in tek bir karakterine, listede olduğu gibi, 0’dan başlayan indeksle erişirsiniz. Python’da negatif indeks de vardır ve sondan sayar: -1 son karakter, -2 sondan ikinci karakterdir. C’de -1 indeksi dizinin dışına taşardı. "Python" metninin indeksleri şöyle:
P y t h o n
0 1 2 3 4 5
-6 -5 -4 -3 -2 -1
word = "Python" için word[0] P, word[-1] ise n karakteridir. İndeks metnin dışına çıkarsa, örneğin word[6] yazarsanız, IndexError alırsınız.
Dilimleme: metnin bir parçasını almak
Köşeli parantezin içine iki nokta koyarsanız tek bir karakter yerine metnin bir parçasını alırsınız. Buna dilimleme (slicing) denir. word[başlangıç:bitiş] yazımı range() gibi çalışır: başlangıç dâhil, bitiş hariç. Başlangıcı yazmazsanız metnin başından, bitişi yazmazsanız metnin sonuna kadar alınır.
Yukarıdaki indeks tablosuna bakın. Hücredeki dört satır ne yazar?
Dilimin de range() gibi üçüncü bir değeri var: adım. word[başlangıç:bitiş:adım] yazımında adım 2 ise bir karakter alınır, bir karakter atlanır. Adım negatifse dilim sondan başa doğru ilerler. Başlangıç ve bitiş boş kalırsa bütün metin dolaşılır. Bu üç satır ne yazar?
Tersten okunuşu kendisiyle aynı olan sözcüğe palindrom denir, kek ve ses gibi. Hücreye print(word == word[::-1]) satırını ekleyin, word değerini önce "kek", sonra "kel" yapıp çalıştırın.
Tek indeks metnin dışına çıkınca hata verir. Dilimin sınırı metnin dışına çıkarsa ne olur? Önce tahmin edin:
Dilim, metnin dışına taşan sınırı metnin ucunda keser ve hata vermez. Alınacak karakter kalmazsa boş bir string döndürür. Tek indeks ise metnin içinde olmak zorundadır.
Dilim de yeni bir string döndürür, word değişmez. Dilimleme listelerde de aynı biçimde çalışır: scores[1:] ilk eleman dışındaki elemanlarla yeni bir liste döndürür.
String metotları yeni bir string döndürür
String’lerin hazır metotları da vardır. Metot, bir değerin arkasına nokta koyup çağırdığınız fonksiyondur, text.strip() gibi. Aşağıdaki metnin başında ve sonunda ikişer boşluk var. Son satır ne yazar?
String değiştirilemez (immutable): oluşturulduktan sonra içindeki karakterler değişmez. text[0] = "P" yazarsanız TypeError alırsınız. text.upper() de var olan string’i değiştirmez, büyük harfli yeni bir string döndürür. Bunu bilerek tahmin edin: hücredeki iki print() ne yazar?
İkinci satırdaki text.upper() yeni bir string döndürür, ama hücre bu değeri hiçbir ada bağlamıyor. Sonucu saklamak için dördüncü satırdaki gibi text = text.upper() yazılır.
Listelerde durum farklı. Liste bölümündeki append() yeni bir liste döndürmedi, var olan listeyi değiştirdi, çünkü listeler değiştirilebilir. Bu farkı ve iki adın aynı listeyi gösterdiği durumları gelecek hafta göreceğiz.
Sık kullanılan string araçları
| Araç | Ne yapar | Örnek |
|---|---|---|
strip() |
baştaki ve sondaki boşlukları, satır sonunu siler | " a \n".strip() |
split(sep) |
metni sep geçen yerlerden böler, parçaları liste olarak döndürür |
"Ada,80".split(",") |
split() |
ayırıcı verilmezse metni boşluklardan böler | "bir iki üç".split() |
sep.join(parts) |
parçaları aralarına sep koyarak birleştirir |
", ".join(names) |
replace(a, b) |
a geçen her yere b koyar |
text.replace("-", " ") |
startswith(prefix) |
metnin verilen parçayla başlayıp başlamadığına bakar | name.startswith("A") |
in |
bir parçanın metinde geçip geçmediğine bakar | "py" in "python" |
Bir satırı parçalayın, sonra birleştirin
Dosyadan okunan bir satırın başında ya da sonunda çoğu zaman boşluk, en sonunda da satır sonu karakteri (\n) bulunur. Satırı önce strip() ile temizler, sonra split() ile bölersiniz. Çalıştırmadan önce düşünün: listedeki 80 bir sayı mı olur, metin mi?
line.strip().split(",") satırında önce strip() çalışır, split() temizlenmiş metni böler. Parçalar yine metindir: '80' bir sayı değil, iki karakterlik bir string. Hesap yapacaksanız int() ile çevirirsiniz. join() ters yönde çalışır ve listedeki parçaları aralarına " | " koyarak tek bir metinde birleştirir. join() ayırıcının metodudur, bu yüzden ayırıcı başta yazılır.
5. haftada dosyadan okunan satırları parçalarken strip() ve split()’i sık kullanacağız.
Açılıştaki döngünün Python hâli
Bölümün başındaki program Python’da şöyle yazılır. Notlar bu kez bir metin satırından geliyor:
Çalıştırın ve çıktıyı açılıştaki C programının çıktısıyla karşılaştırın. Kodda değişen yerler şunlar:
isayacı yok.enumerate()her turda sıra numarasını ve parçayı birlikte verir. Sıra numarası0’dan başladığı için ekranaindex + 1yazılır.-1kontrolü döngünün koşulundan çıkıp gövdeye girdi.forsatırı bir koşul almaz, verinin sonuna kadar dolaşır.-1’de durmak içinbreakgerekir, sonraki90’a sıra gelmez.continueC’deki gibi çalışır ve sınava girmeyen öğrencinin turunu atlar.else ifyerineelifyazılır.70,score >= 70dalına düşer veBBalır.count++yerinecount += 1yazılır.- Dizi yok.
line.strip().split(",")satırı temizler ve virgüllerden böler. Parçalar metin olduğu için her biriint()ile sayıya çevrilir.
Bölümün başında yazdığınız listeyle karşılaştırın. C kodunu satır satır çevirmek yerine işin Python’da nasıl yazıldığına bakın. İndekse ihtiyacınız yoksa değerleri doğrudan dolaşın, hem indeks hem değer gerekiyorsa enumerate() kullanın.
Alıştırma: Koşulu tamamla
Bir kullanıcı adı en az 5 karakter olmalı ve boşluk içermemelidir. Boşluğa bir ifade yazın: iki koşul da sağlanıyorsa valid True, sağlanmıyorsa False olsun. Sonra username değerini "ece" ve "emre can" yapıp yeniden çalıştırın.
len(username) >= 5 ile " " not in username ifadelerini and ile birleştirebilirsiniz.
valid = len(username) >= 5 and " " not in usernameAlıştırma: Doğrudan dolaşarak sayma
Aşağıdaki kod bir metindeki a harflerini sayıyor. Çalıştırmadan önce sonucu tahmin edin.
count bir sayaç, ama indeks tutmuyor. Kaç tane a bulduğumuzu sayıyor, karakterleri ise for kendisi veriyor. Sonra şunları deneyin:
- Kodu büyük ve küçük harfi ayırmadan sayacak biçimde değiştirin.
"Ankara"için sonuç3olmalı. textdeğerini"Iğdır"ve"İzmir"yapın,print(text.lower())ile ne çıktığına bakın.- Bu yöntemin Türkçe metinlerde neden her zaman doğru sonuç vermediğini bir cümleyle açıklayın.
Alıştırma: Off-by-one hatasını düzelt
Aşağıdaki program 1’den 10’a kadar sayıların toplamını hesaplamalı, ama sonuç eksik çıkıyor. Bu tür hataya off-by-one hatası denir: sınır bir eksik ya da bir fazla alınmıştır.
Neden 10 işlenmiyor? Yalnızca tek bir değeri değiştirerek düzeltin. Doğru toplam 55’tir.
Alıştırma: while döngüsünü güvenli hâle getir
Aşağıdaki kod yanlış tahminleri sayıyor ve üçüncü denemede duruyor. Önce çalıştırın.
Sonra şu iki soruyu cevaplayın:
breaksatırını silerseniz ne olur? Koşuldaki hangi değer döngü içinde hiç değişmiyor?- Aynı sınırı
breakkullanmadan nasıl koyarsınız?whilesatırındaki koşulaandileattempts < 3ekleyin,ifvebreaksatırlarını silin.
İkinci değişikliği hücrede deneyin, çıktı yine üç satır olmalı. Birinci soruyu hücrede denemeyin, çalıştırmadan cevaplayın. Sonsuz döngü durmadan yazdırır ve sayfayı yenilemeniz gerekebilir.
Sıra sizde: Metin özeti
Bir metnin aşağıdaki bilgilerini yazdıran bir program yazın:
- Toplam karakter sayısı
- Boşluk sayısı
- Sesli harf sayısı (bir karakterin sesli harf olup olmadığına
ch in "aeıioöuüAEIİOÖUÜ"ile bakabilirsiniz) split()ile kelime sayısı- İlk boşluğun indeksi (
enumerate()ile dolaşın, ilk boşluktabreakile durun, boşluk yoksa bunu yazdırın) strip()ile baştaki ve sondaki boşluklar temizlendikten sonra ilk ve son karakter
Metni bir değişkene yazın, input() kullanmanız gerekmez. Programda en az bir for, bir if, bir enumerate() ve iki farklı string metodu bulunsun.
Temizlenmiş metin boşsa text[0] veya text[-1] yazmak IndexError verir. Yalnızca boşluklardan oluşan bir metin de strip() sonrasında boş kalır. İlk ve son karaktere erişmeden önce metnin boş olup olmadığını kontrol edin.
Çalışır kod görevi: Programınızı en az normal metin, boş string ve yalnızca boşluklardan oluşan metinle çalıştırın. Üçünde de programın hata vermeden bitip beklenen sonucu yazdığını gösterin.
Tek sayfa özet
ifvewhileC’dekiyle aynı işi yapar, yalnız yazımları daha sadedir.else ifyerineelifyazılır.elifzincirinde yalnız ilk doğru koşulun bloğu çalışır. Ayrı ayrı yazılanifsatırlarının her biri kendi başına sınanır.- Liste köşeli parantezle yazılır ve değerleri sırayla tutar. İndeks
0’dan başlar,append()sona eleman ekler. forbir listenin ya da metnin elemanlarını sırayla verir. İndeks gerekmiyorsa sayaç tutmazsınız.- Hem indeks hem değer gerektiğinde
enumerate(), iki listeyi birlikte dolaşırkenzip()kullanılır. breakdöngüyü bitirir;continueyalnızca mevcut turu atlar.range()son sınırı içermez ve üçüncü parametre adım değeridir.- Dilimleme
text[başlangıç:bitiş:adım]biçiminde yazılır verange()gibi bitişi dışarıda bırakır. Negatif indeks sondan sayar,text[::-1]metni ters çevirir. strip()satırı temizler,split()parçalara ayırır,join()parçaları yeniden birleştirir.split()her zaman metin parçaları döndürür, sayı içinint()gerekir.- String değiştirilemez (immutable).
upper()gibi metotlar yeni bir string döndürür, sonucu saklamak için bir ada bağlamanız gerekir. - Python’da da sınır değerlerine ve off-by-one hatalarına dikkat etmek gerekir.
Gelecek hafta listeleri ve öteki Python koleksiyonlarını göreceğiz. İki değişkenin aynı listeyi gösterdiği durumlar (referans) ve bir değerin yerinde değiştirilip değiştirilemeyeceği (değiştirilebilirlik) nesne tabanlı programlamada sık karşımıza çıkacak.
Bu bölümün kazanımları
Bu bölümü bitiren öğrenci:
if/elif/elsezincirinde sınır değerinin hangi dala düştüğünü belirler.- Bir listenin ya da metnin elemanları üzerinde indeks kullanmadan dolaşır;
enumerate()ilezip()’in hangi durumda gerektiğini ayırt eder. range()bitiş değerinin dışarıda kaldığını bilir ve adım parametresini kullanır.whiledöngüsünde koşuldaki değeri her turda değiştiren adımın sonsuz döngüyü önlediğini açıklar.breakilecontinuearasındaki farkı gösterir.- String’in değiştirilemez olduğunu ve
upper()gibi metotların yeni bir string döndürdüğünü açıklar. strip(),split()vejoin()ile bir metin kaydını parçalara ayırır ve birleştirir.- Dilimlemede başlangıç, bitiş ve adım değerlerinin metnin hangi karakterlerini seçtiğini belirler.