2.2. For Döngüleri

Kayıt Tarihi:

Son Güncelleme:

Özet:

Bu derste for döngülerini açıklayacağız, bu döngüleri öğrenmek için önce list ve tipi verileri anlamamız gerekiyor. Bu tür veriler üzerinde yapılabilen temel işlemleri açıkladıktan sonra bazı döngü örnekleri vereceğiz.

Anahtar Kelimeler: Binom dağılımı · dilimleme · for döngsü · index · indis · list · list comprehension · slice · tuple

List Verileri

Programlarımızda sıklıkla tek bir nesne ile çalışmak yerine birden fazla nesneyi bir grup haline getirerek bütün bir grup ile işlemler yaparız. Python'da böyle nesne grupları oluşturmanın bir yolu list (liste) verileri oluşturmaktır. Python'da list verisi oluşturmanın bir yolu bazı nesneleri köşeli parantez içinde belirli bir sıra dahilinde virgülle ayırarak yazmaktır.


>>> L = [0, 2, 4, 6, 8, 10]
>>> print L
[0, 2, 4, 6, 8, 100]
>>> type(L)
type 'list'>

Bu tanımladığımız list nesnesinin tüm elemanları birer int nesnesidir ama genelde böyle bir kural yoktur, elemanlar farklı tipten veriler de olabilir. Yukarıda tanımladığımız L değişkeni, altı tane veriden oluşan list tipi veriyi temsil ediyor.

Bir listenin her bir öğesinin bir indisi (index) vardır, ilk öğenin indisi 0, sonrakinin 1, ve böyle devam eder. Bir listenin tekil bir elemanına ulaşmak için o öğenin indisini kullanarak liste[indis] komutunu gireriz. Python'da alternatif olarak farklı bir indisleme sistemi daha vardır. Bu sisteme göre listenin son elemanının indisi -1, bir öncekinin indisi -2, ve diğerleri de böyle devam eder. İstenilirse baştan istenilirse sondan indisleme sistemi kullanılarak işlem yapılabilir. Bu şekilde indisleme yöntemiyle bir list nesnesinin elemanları üzerinde manipülasyon yapabiliriz, elemanları veya sıralarını değiştirebiliriz.


>>> L
>>> [0, 2, 4, 6, 8, 10]
>>> L[0]
0
>>> L[1]
2
>>> L[4]
8
>>> L[-1]
10
>>> L[-5]
2
>>> L[-1] = 100
>>> L
[0, 2, 4, 6, 8, 100]

Bu L nesnesinin sonuna yeni bir eleman eklemek için L.append(nesne) komutunu gireriz. Herhangi bir i indisli konuma veni bir nesne eklemek için se L.insert(i, nesne) komutunu kullanırız, bu durumda daha sonraki elemanların indislerin değişeceğine dikkat edin. Bu L nesnesinin bir elemanının indis numarasını öğrenmek için L.index(nesne) komutundan faydalanırız, herhangi bir i indisli elemanı silmek için del L[i] komutunu kullanırız. Ayrıca bir listenin eleman sayısı bilgisi len(L) komutu ile getirilebilir. x in L ifadesi bir bool verisidir ve x nesnesi L listesinin bir elemanı ise True, aksi taktirde False değerini döndürür.


>>> L
[0, 2, 4, 6, 8, 100]
>>> len(L)
6
>>> L.append("python")
>>> L
[0, 2, 4, 6, 8, 100, 'python']
>>> L.index("python")
6
>>> "python" in L
True
>>> del L[-1]
>>> "python" in L
False
>>> L
[0, 2, 4, 6, 8, 100]
L.insert(1, "python")
>>> L
[0, 'python', 2, 4, 6, 8, 100]

Python list nesneleri üzerinde toplama, çıkarma ve çarpma gibi bazı aritmetik işlemler de tanımlanmıştır, aşağıdaki örneklerde bunların nasıl davrandığını gözlemleyebilirsiniz.


>>> L
[0, 'python', 2, 4, 6, 8, 100]
>>> L1 = [0]
>>> L2 = 5*L1
>>> L2
[0, 0, 0, 0, 0]
>>> L3 = L + L2
>>> L3
[0, 'python', 2, 4, 6, 8, 100, 0, 0, 0, 0, 0]

Sıklıkla elemanları belirli bir düzene göre sıralanmış sayı listelerine ihtiyacımız olacak, böyle listeleri yukarıdaki örneklerde yaptığımız gibi açıkça tanımlamak yerine bir döngü yardımıyla kolayca oluşturabiliriz. Bunun için önce boş bir liste oluşturup döngü içinde her bir elemanı hesaplayıp listenin sonuna ekleriz.


L = [] #bos bir list

a = 0
b = 30

while a <= b:
    L.append(a)
    a += 3

print L

Bu program 0 ile 30 arasında (0 ve 30 dahil) 3'ün katı olan tamsayıların bir listesini oluşturup yazdırır. Programın çıktısı aşağıdaki gibi olacaktır.


>>> python list1.py
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30]

Bu işlemin çok sık yapıldığı için Python'da bu işlem için hazır bir range(a, b, s) fonksiyonu vardır. Bu fonksiyon a ile b sayıları arasında (b dahil değil) s aralıklı sayıların bir listesini oluşturur. Bu komutun bir kaç farklı kullanım yöntemi daha vardır.

  • range(n) = range(0, n, 1) = [0, 1, 2, .., n-1]
  • range(n, k) = range(n, k, 1) = [n, n+1, n+2, .., k-1]

Bunları aşağıdaki örnekleri uygulayarak gözlemleyebilirsiniz.


>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(3, 8)
[3, 4, 5, 6, 7]
>>> range(-5, 12, 3)
[-5, -2, 1, 4, 7, 10]

Tuple Verileri

Python'da listelere çok benzeyen bir de tuple verileri vardır. Bu veri tipi list veri tipine çok benzerdir, bunlar da birden fazla elemanı olan sıralı verilerdir. Köşeli parantez yerine normal parantez içinde yazılırlar ve tanımlandıktan sonra elemanları değiştirilemez. Bu veriler de list verileri gibi kullanılırlar, indisleme ve döngü içinde kullanımları tamamen aynıdır. tuple verileri üzerinde işlemler genelde list verilerine çok daha hızlıdır, dolayısıyla elemanların sonradan güncellenmesi gerekmiyorsa bu verileri kullanmak program verimliliği açısından önemlidir. Ayrıca daha sonra değişmemesi gereken verileri korumak için list yerine tuple verileri kullanılabilir.


>>> T = (1, 3, 5, 7, 9)
>>> type(T)
type 'tuple'>
>>> T[2]
5
>>> T[-2]
7
>>> 3 in T
True
>>> T.append(11)
Traceback (most recent call last):
File "", line 1, in 
AttributeError: 'tuple' object has no attribute 'append'

Dilimleme ve Birleştirme

Listenin bir kısmının seçilmesiyle oluşan yeni listelere bir alt liste (sublist) veya dilim (slice) denir. Bir listeden bir parçayı seçerek bu yeni listeleri oluşturma yöntemine dilimleme (slicing) denir.

  • A[i:] komutu A listesinin i indisli elemanından başlayıp listenin sonuna kadar tüm elemanların listesini oluşturur.
  • A[:j] komutu A listesinin başından başlayıp j indisli elemana kadar (j dahil değil) tüm elemanların listesini oluşturur.
  • A[:] komutu A listesinin tüm elemanlarından oluşan bir liste oluşturur.
  • A[1:-1] komutu A listesnin ilk ve son elemanı hariç diğer tüm elemanlarından oluşan bir liste oluşturur.
  • A[i:j:s] komutu A listesnin i ve j indisli elemanları arasında (j dahil değil) s adım aralıklı olarak bir liste oluşturur.


>>> A = range(15)
>>> A
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> A[:7]
[0, 1, 2, 3, 4, 5, 6]
>>> A[8:]
[8, 9, 10, 11, 12, 13, 14]
>>> A[:]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> A[2: 9: 2]
[2, 4, 6, 8]
>>> A[2: 9: 2][:2]
[2, 4]

Python'da bazen birden fazla listenin elemanlarıyla senkronize bir biçimde çalışmamız gerekebilir, böyle durumlarda zip(list1, list2) komutunu kullanırız. Bu komut her bir elemanı (x1, y1) tuple nesneleri olan bir list üretir, bu tuple nesnelerinin ilk elemanı list1 ve diğer elemanı da list2 listesinin elemanıdır.


from math import sin, pi

L1 = [5, 10, 15, 20]
L2 = [pi, 2*pi, 3*pi, 4*pi]
L3 = zip(L1, L2)

k = 0
while k < len(L1):
    A = L3[k][0]
    B = L3[k][1]
    print "A=%2d, B=%7.4f, A*sin(B)=%9.2e" % (A, B, A*sin(B))
    k += 1

Bu programın çıktısı aşağıdaki gibi olacaktır. Burada zip() komutunu kullanmadan daha basit şekilde de bu çıktıyı alabilirdik. Bunun için 5. satırı silip 8 ve 9 numaralı satırları sırasıyla A = L1[k] ve B = L2[k] olarak değiştiririz, böylece zip() komutunu kullanamdan da ayı işi yapmış oluruz.


Terminal > python list2.py
A= 5, B= 3.1416, A*sin(B)= 6.12e-16
A=10, B= 6.2832, A*sin(B)=-2.45e-15
A=15, B= 9.4248, A*sin(B)= 5.51e-15
A=20, B=12.5664, A*sin(B)=-9.80e-15

Döngü Yapısı

Bir list veya tuple nesnesinin tüm elemanlarına aynı işlemi uygulayamak için for döngülerinden faydalanırız. Bu döngü çeşidini bir örnek üzerinde çalışalım. Farz edelim ki başarı olasılığı $p$ (dolayısıyla başarısızlık olasılığı $1-p$) olsun. Bu durumda $n$ deneme yapalım ve her bir denemenin sonucu önceki denemelerden bağımsız olsun. Bu $n$ deneme sonucunda $x$ tane başarı (dolayısıyla $n-x$ tane başarısızlık) elde etme olasılığımız $$ B(x,n,p)=\frac{n!}{x!(n-x)!}p^x (1-p)^{n-x}$$ formülü ile verilir, bu formüle Binom dağılımı denir. Bir madeni parayı art arda fırlattığımızı düşünelim, tura gelmesi olayını başarı kabul edersek bu durumda bu olayın gerçekleşme olasılığı $p=1/2$ olacaktır. Örneğin beş denemede iki defa tura gelme olasılığı $B(2, 5, 0.5)$ değerine eşittir. Amacımız $n = 10, 20, 30, 40, 50$ deneme sonucunda başarı olasılıklarını hesaplayıp yazdıran bir program yazmaktır. Burada $n!$ ifadesi $n$ doğal sayısının faktöriyel değeridir ve bu hesaplamayı math modülü içinde tanımlanmış math.factorial() fonksiyonu yardımıyla yapabiliriz.


from math import factorial as fact

nlist = range(10, 51, 10) #[10, 20, 30, 40, 50]
p = 0.5
x = 2.0

for n in nlist:
    B = fact(n)/(fact(x)*fact(n-x))*p**x*(1-p)**(n-x)
    print "%d deneme -> %5.3e ihtimal" % (n, B)

Programda kullandığımız for döngüsü adımlarını açıklayalım. Döngüyü for a in L biçiminde başlatıyoruz, bunun anlamı aşağıda farklı paragraf hizasından başlayan tüm satırlar L listesinin her bir elemanı için ayrı ayrı çalıştırılacak demektir. Daha sonra iki nokta üst üste koyup altında hizalanmış biçimde hesaplamaları ve komutları yerleştiriyoruz. Bu programın çıktısı aşağıdaki gibi olacaktır.


Terminal > for1.py
10 deneme -> 4.395e-02 ihtimal
20 deneme -> 1.812e-04 ihtimal
30 deneme -> 4.051e-07 ihtimal
40 deneme -> 7.094e-10 ihtimal
50 deneme -> 1.088e-12 ihtimal

Bu programı daha kompakt ve verimli bir biçimde aşağıdaki gibi de kodlayabiliriz, bu da aynı çıktıyı üretecektir.


from math import factorial as fact

p = 0.5; x = 2.0

for n in xrange(10, 51, 10):
    B = fact(n)/(fact(x)*fact(n-x))*p**x*(1-p)**(n-x)
    print "%d deneme -> %5.3e ihtimal" % (n, B)

Burada kullandığımız xrange() fonksiyonu bu örnekte aynı işi yapmasına rağmen aslında range fonksiyonundan oldukça farklıdır. range() fonksiyonu istenilen değerleri içeren bir liste oluşturup bellekte tutarken xrange() fonksiyonu açıkça bir liste oluşturup bellekte yer kaplamaz, döngünün her bir adımında sıradaki elemanını çıktı olarak döndürür. Dolayısıyla döngülerde bu fonksiyonun kullanımı çok daha verimlidir, eğer oluşturulan listeyi daha sonra başka işlemlerimizde kullanmayacaksak xrange() fonksiyonunu tercih etmeliyiz. Python 3 versiyonunda bulunan xrange() adında bir fonksiyon yoktur çünkü orada range() fonksiyonu Python 2'deki xrange() gibi davranır.


>>> a = range(10, 51, 10)
>>> a
[10, 20, 30, 40, 50]
>>> type(a)
type 'list'>
>>> b = xrange(10, 51, 10)
>>> b
xrange(10, 60, 10)
>>> type(b)
type 'xrange'>

Her for döngüsünü bir while döngüsü olarak yeniden kodlayabiliriz. Bunun için önce n = 0 biçiminde bir sayaç değişkeni tanımlarız, daha sonra for a in L ifadesini while n < len(L) ifadesiyle değiştiririz. Döngü komutlarında ise üzerinde işlem yapacağımız elemanı x = L[n] komutuyla alıp üzerinde gerekli işlemleri yaparız. Son olarak döngü içinde sayaç değerini arttırırız. Örneğin yukarıdaki program bir while döngüsü kullanılarak aşağıdaki gibi yazılabilir.


from math import factorial as fact

nlist = range(10, 51, 10) #[10, 20, 30, 40, 50]
p = 0.5
x = 2.0

i = 0
while i < len(nlist):
    n = nlist[i]
    B = fact(n)/(fact(x)*fact(n-x))*p**x*(1-p)**(n-x)
    print "%d deneme -> %5.3e ihtimal" % (n, B)
    i += 1

Python'da sıklıkla bir listenin öğelerini sayaç olarak kullanarak başka bir liste oluşturma işlemi yapılır ve dolayısıyla bunun için bir kısa yol vardır. Bu işlemi birkaç örnek üzerinde görelim, aşağıda görüleceği gibi bu işlemi köşeli parantezler içinde tek satırda yazılmış bir for döngüsü gibi değerlendirebiliriz. Bu işleme liste kapsama (list comprehension) denir.


>>> from math import factorial
>>> N = [1, 3, 5, 7, 9]
>>> L = [factorial(n) for n in N]
>>> L
[1, 6, 120, 5040, 362880]

Bazen tablosal bir veriyi saklayıp yazdırmamız gerekebilir, bunu iç içe listeler kullanarak yapabiliriz. Yani elemanları yine birer liste olan bir liste oluşturabiliriz. Genelde bir tabloyu bellekte tutmak için tablonun her satırını ayrı birer liste olarak kaydedip daha sonra bu satırları eleman olarak kabul eden bir ana liste oluştururuz. Dolayısıyla bu son liste tüm tablo verisini içerecektir. Örnek olarak Celsius ve Fahrenheit derece arası dönüşüm formülü olan $$F = \frac{9}{5}C + 32$$ formülünü ele alalım. Bir sütunu Celsius, diğer sütunu da bunlara karşılık gelen Fahrenheit büyüklüklerini içeren bir tablo yazdıralım. Bunu aşağıdaki program ile sağlayabiliriz.


Clist = range(10, 36, 5)                      #[10, 15, 20, ... , 35]
Flist = [9.0/5*C + 32 for C in Clist]         #F degerleri
L = [[C, F] for C, F in zip(Clist, Flist)]    #ana tablo

for C, F in L:
    print C, F

Burada yazdırma aşamasında C ve F değerlerini aynı anda L listesinden nasıl aldığımıza dikkat edin. L listesinin her bir elemanı bir [C, F] ikilisi biçiminde, dolayısıyla döngünün her bir adımı böyle bir ikiliyi ziyaret ediyor ve iki değişkeni birden alıp yazdırıyor. Programın çıktısı aşağıdaki gibi olacaktır.


Terminal > for4.py
10 50.0
15 59.0
20 68.0
25 77.0
30 86.0
35 95.0
Önceki Ders Notu:
2.1. While Döngüleri
Dersin Ana Sayfası:
Python ve Bilimsel Hesaplama
Sonraki Ders Notu:
2.3. If-Else Koşullu Yapısı