AST415 Astronomide Sayısal Çözümleme - I

Ders - 04 Şartlı İfadeler ve Fonksiyonlar

Doç. Dr. Özgür Baştürk
Ankara Üniversitesi, Astronomi ve Uzay Bilimleri Bölümü
obasturk at ankara.edu.tr
http://ozgur.astrotux.org

Fonksiyonlar

Programlama dillerinde fonksiyon matematikteki fonksiyonlardan daha farklı bir anlam taşır. Fonksiyon bir programda istediğiniz zaman ve istediğiniz yerde kullanabileceğiniz ifadeler bütünüdür. Fonksiyona isterseniz nesneler yollayarak fonksiyon içerisindeki kullanımlarıyla hesaplamaları etkileyebilir ve fonksiyondan programa nesneler döndürebilirsiniz.

Fonksiyonlar belirli bir işi yapan bir kod bütününü birden fazla kez yazmayı önler. Bu strateji sizi defalarca kez aynı şeyi yazmaktan ya da kopyalamaktan ve kodunuzun giderek daha fazla yer kaplamasından kurtarır.

Ayrıca fonksiyonlar, tekrarlama gerektiren işlerde yapacağınız bir değişiklikte, tüm bu kodları tek tek bulup değiştirmeniz yerine tek bir yerde (fonksiyon yapısı içinde) ve kolayca değiştirmenize de olanak sağlarlar.

Ayrıca programınızı daha küçük parçalara bölmeniz sayesinde daha rahat yönetebilir ve daha “temiz” düşüebilirsiniz. Bu bölümde daha önce gördüğünüz $sqrt$, $len$, $range$ gibi fonksiyonlara ilaveten kendi fonksiyonlarınızı nasıl yazacağınızı öğreneceksiniz.

Tek Değişkenli Fonksiyonlar

Önceki derslerden Santigrat derece / Fahrenheit derece dönüşümünü hatırlıyorsunuz. Böyle bir dönüşüm için bir fonksiyon yazmak akılcıdır. Çünkü bu dönüşümü programlarınız içinde defalarca kullanmanız gerekebilir.

In [1]:
def F(C):
    fahr = (9.0/5)*C + 32
    return fahr

Kullanıcı tarafından yazılan tüm fonksiyonlar $def$ ifadesiyle, yani fonksiyonun tanımıyla başlar. Daha sonra fonksiyonun adı (örneğimizde $F$), parantez içerisinde fonksiyona programdan gönderilen nesne(ler)in (argümanlar) fonksiyon içerisinde kullanılacak isimleri (birden fazla olması durumunda virgülle ayrılır) ve en sonda da ":" işareti bulunur. Döngülerden de hatırlayacağınız gibi bu işaret (:) yorumlayıcıya yeni bir kod bloğuna geçeceğinizi söyler. Dolayısı ile fonksiyonlarda da tıpkı döngülerde olduğu gibi, fonksiyon içerisinde çalışması istenen tüm ifadeler bir blok \"<TAB>" ya da 4 boşluk standarttır) içeri yazılır.

Her fonksiyon için zorunlu olmasa da çoğu fonksiyon programa nesne(ler) de döndüebilir. Bunun için $return$ ifadesi kullanılır. Birden fazla nesne döndürülmek istendiğinde bu nesneler virgüllerle birbirinden ayrılır.

$def$ ile başlayan satır fonksiyonun başlığı (header), bir blok içeri yazılan her şey ise fonksiyonun yapısı (body) olarak adlandırılır.

Başa Dön

Fonksiyon Çağırmak

Bir fonksiyonu kullanmak ve ondan değer alabilmek için o fonksiyonu “çağırmanız" gerekir. Tipik birkaç örnek aşağıda verilmiştir.

In [2]:
santigrad = 10
fahrenheit = F(10) # ile asagidaki ifade aynidir
fahrenheit = F(santigrad) # zira santigrad 10 olarak tanimlanmis
print(fahrenheit)
print(F(santigrad+10.0))
toplam_sicaklik = F(10) + F(20)
print(toplam_sicaklik)
Cderece = range(-5,41,5)
Fderece = [F(C) for C in Cderece]
print(Fderece)
50.0
68.0
118.0
[23.0, 32.0, 41.0, 50.0, 59.0, 68.0, 77.0, 86.0, 95.0, 104.0]

İstenildiği takdirde fonksiyon formatlı bir metin döndürecek şekilde yazılabilir, fonksiyonun içinde de nesneler tanımlanabilir.

In [1]:
def F2(sant):
    fahr = (9.0/5)*sant + 32
    return "{:.1f} santigrad derece {:.1f} fahrenheit derecedir".format(sant, fahr)
metin = F2(21.0)
print(metin)
21.0 santigrad derece 69.8 fahrenheit derecedir

Yerel ve Global Nesneler

Bir önceki örnekte $F2$ fonksiyonu içerisinde yarattığımız $fahr$ değişkeni fonksiyonun içinde yerel bir nesnedir. Bu nesnenin değerine fonksiyonun dışında programın herhangi başka bir yerinden ulaşmak istenirse bir hata mesajı alınır.

In [ ]:
santigrad = 21.0
metin = F2(santigrad)
print(metin)
print(fahr)

Yerel değişkenler fonksiyonun içinde yaratılırken, fonksiyon terk edildiği anda “yok edilirler”. Bu olguyu anlamak için aşağıdaki örneği inceleyelim.

In [ ]:
def F3(C):
    F_deger = (9.0/5)*C + 32
    print('F3 fonksiyonunda: C={:.1f}, F_deger={:.1f}, r={:d}'.format(C, F_deger, r))
    return '{:.1f} derece santigrad {:.1f} fahrenheit derecedir.'.format(C, F_deger)
C = 60 # C global tamsayi nesnesini olusturalim.
r = 21 # bir baska global nesne
s3 = F3(r)
print(s3)
print(C)

Aynı isme sahip birden fazla nesneniz olduğunda Python öncelikle yerel nesneler arasında bir arama yapar, sonra global değişkenlere bakar ve son olarak da hazır (built-in: kullanıcı tarafından tanımlanmamış) Python fonksiyonlarına bakar. Aşağıda bu kural, $sum$ isminin birden fazla nesneye verildiği bir örnekle anlatılmaktadır.

In [4]:
print(sum) # sum hazir (built-in) bir Python fonksiyonudur
sum = 500 # sum burada bir global tamsayi nesnesi olarak tanimlanmaktadir
print("Fonksiyonun disinda global tamsayi nesnesi sum: ",sum)
def fonk1(n):
    sum = n + 1
    print("Fonksiyonun icinde yerel nesnesi sum: ", sum) # sum bir yerel nesne olarak tanimlanmaktadir
    return sum
sum = fonk1(2) + 1 # global nesne sum'a yeni deger ataniyor
print("Fonksiyonun disinda onun gonderdigi degeri tutan global nesne sum: ", sum)
<built-in function sum>
Fonksiyonun disinda global tamsayi nesnesi sum:  500
Fonksiyonun icinde yerel nesnesi sum:  3
Fonksiyonun disinda onun gonderdigi degeri tutan global nesne sum:  4

Global nesnelere bir fonksiyon içerisinden erişmek mümkün olduğu halde (gördüğünüz gibi!), onların değerini fonksiyon içerisinde değiştirmek için değişkenin global olduğunu ayrıca belirtmeniz gerekir. Bu uygulama global değişkenlerin özellikle uzun programlar içinde kontrol etme zorluğu nedeniyle önerilmez, ancak gerçekten gerektiğinin düşünüldüğü durumlarda ve dikkatli bir şekilde, iç dokümantasyonla kontrol altında tutularak kullanılmalıdır.

In [5]:
a = 20; b = -2.5    # global tamsayi ve kayan sayi nesneleri

def f1(x):
    a = 21          # a yeni bir yerel tamsayi nesnesi olarak yaratiliyor
    return a*x + b  # 21*x - 2.5 degeri dondurecek

print("a = ", a)    # a 20 degerini alir, zira 21 degerine f1 icinde atandi!
                    # alacagi deger global degeri olacaktir.

def f2(x):
    global a        # simdi a'nin global bir nesne oldugu belirtiliyor
    a = 21          # ve bu nedenle degeri tum program icin
                    # fonksiyon icinden dahi degistirilebiliyor
    return a*x + b  # Yine 21*x - 2.5 degeri dondurecek

print("f1(3) = ", f1(3))
print("a = ", a)    # a icin 20 basilir
                    # zira henuz a degerini global olarak degistiren
                    # f2 cagirilmadi
print("f2(3) = ", f2(3))
print("a = ", a)    # a icin 21 basilir zira f2 a degiskeninin degerini
                    # global olarak degistirebiliyor!
a =  20
f1(3) =  60.5
a =  20
f2(3) =  60.5
a =  21

Fonksiyona Birden Fazla Nesne Göndermek

Şu ana kadar tanımladığımız fonksiyonlar argüman olarak sadece bir nesne alıyorlardı. Oysa ki Python'da bir fonksiyona istediğiniz sayıda nesne geçirebilirsiniz. Bunun için tek yapmanız gereken argüman olarak geçirmek istediğiniz nesnelerin isimlerini virgüllerle birbirinden ayırmaktır.

In [7]:
def fonk(t, v0):
    g = 9.81          
    return v0*t - 0.5*g*t**2
# Bu fonksiyonu asagidaki sekillerde cagirabilirsiniz
# Hepsi ayni degeri (0.55095) basacaktir
y = fonk(0.1, 6)
print("y = {:.5f}".format(y))
y = fonk(0.1, v0=6)
print("y = {:.5f}".format(y))
y = fonk(t=0.1, v0=6)
print("y = {:.5f}".format(y))
y = fonk(v0=6, t=0.1)
print("y = {:.5f}".format(y))
y = 0.55095
y = 0.55095
y = 0.55095
y = 0.55095

Uyarı: Fonksiyonu çağırırken $arguman = deger$ yapısını kullanmak büyük bir esneklik sağlar. Bu şekilde fonksiyona geçirmek istediğiniz tüm argümanları böyle geçirdiğiniz sürece sıralamanın bir önemi kalmaz. Ancak bu yapı yerine sadece değer geçirirseniz, bu değerleri fonksiyonun tanımındaki sıralamaya riayet ederek geçirmek zorunda kalırsınız!

Şu ana kadar sadece matematiksel fonksiyonlara tam ya da reel sayılar geçirip, onlardan tam ya da reel sayı geri dönüşler aldık ya da ekrana değerler bastırdık. Ancak fonkisyonlar bu işlemlerin ötesinde de kullanılabilir.

In [ ]:
def listeyap(baslangic, son, adim):
    # verilen son dahil olmak uzere baslangic ile son arasinda 
    # adim degeri kadar artimlarla liste olusturan fonksiyon
    eleman = baslangic
    liste = []
    while eleman <= son:
        liste.append(eleman)
        eleman += adim
    return liste
listem = listeyap(0, 10, 2)
print(listem)

Bu örnekte $baslangic$, $son$, $adim$, $eleman$ ve $liste$ nesneleri yerel; $listem$ nesnesi ise globaldir.

Başa Dön

Fonksiyondan Birden Fazla Nesne Döndürmek

Şu ana kadar yazdığımız tüm fonksiyonlar tek bir değer döndürüyor. Oysa Python fonkisyonları birden fazla değeri döndürebilir. Dikey atış probleminde cismin $t$ zamanda aldığı toplam yolun yanı sıra bunun zamana göre türevini de (yani t anındaki hızını!) döndürmek istiyor olalım.

In [16]:
def fonk(t, v0):
    g = 9.81
    y = v0*t - 0.5*g*t**2
    dydt = v0 - g*t
    return y, dydt

# Bu fonksiyondan deger dondururken iki degiskene ihtiyac duyariz
konum, v0 = fonk(0.6, 6)
print("Cismin t={:.1f} saniyedeki konumu {:.2f}, hizi {:.2f} tir".format(0.6, konum, hiz))
# Coklu nesne donduren fonksiyonlardan dondurulen bir demet degiskendir
demet = fonk(0.6, 6)
print(demet)
Cismin t=0.6 saniyedeki konumu 1.83, hizi 0.11 tir
(1.8341999999999996, 0.11399999999999988)

Örnekler 1

ln(1 + x) 'in Seriye Açılımı

$$L(x;n) = \sum\limits_{i=1}^n \frac{1}{i} (\frac{x}{1 + x})^i$$$$ln(1 + x) = \lim_{n\to\infty} L(x;n)$$

ln(1 + x)'in yukarıdaki ifadelerle seriye açılması için aşağıdaki bir döngü yapısına ihtiyaç duyulur:

s = 0
for i in range(1, n+1):
    s += (1.0/i)*(x/(1.0 +x ))**i

Aşağıdaki gibi $x$ ve $n$'i birer argüman olarak alıp toplamı döndüren bir fonksiyon işi oldukça kolaylaştırır.

In [ ]:
def L(x, n):
    s = 0
    for i in range(1, n + 1):
        s += (1.0/i)*(x/(1.0 + x))**i
    return s

Bir matematiksel fonksiyonun değerinin seriye açılarak hesaplanmasıyla elde edilen sonuç, $n$ → ∞ olamayacağı için her zaman bir hata taşır. Bu hatayı ve $n$'in herhangi bir değeri için ihmal edilen ilk terimi döndüren bir fonksiyon matematiksel olarak çok daha “fonksiyonel” olacaktır.

In [17]:
def L(x, n):
    s = 0
    for i in range(1, n + 1):
        s += (1.0/i)*(x/(1.0 + x))**i
    toplam = s
    ihmal_edilen_ilk_terim = (1.0/(n + 1))*(x/(1.0 + x))**(n + 1)
    from math import log
    hata = log(1 +x ) - toplam
    return toplam, ihmal_edilen_ilk_terim, hata

# n = 100 ve x = 5, icin ln(1+x)
sonuc, tahmini_hata, tam_hata = L(x = 5, n = 100)
print("Sonuc: {:.2f}, yaklasik hatasi: {:.16f}, hatasi: {:.16f} tir"\
      .format(sonuc, tahmini_hata, tam_hata))
Sonuc: 1.79, yaklasik hatasi: 0.0000000000996260, hatasi: 0.0000000005709522 tir

Değer Döndürmeyen Fonksiyonlar

Tüm fonksiyonlar değer döndürmek zorunda değildir. Bu durumda $return$ ifadesi verilmeyebilir. Bazı programcılık dillerinde (fortran, C) bu tür fonksiyonlara subroutine ya da procedure da denir.

In [19]:
def tablo(x):
    # Deger dondurmeyen fonksiyon
    from math import log
    print('\nx={:g}, ln(1+x)={:g}'.format(x, log(1+x)))
    for n in [1, 2, 10, 100, 500]:
        sonuc, terim, hata = L(x, n)
        print('n={:<4d} {:<10g} (bir sonraki terim: {:8.2e} hata: {:8.2e}'.\
              format(n, sonuc, terim, hata))
tablo(5)
x=5, ln(1+x)=1.79176
n=1    0.833333   (bir sonraki terim: 3.47e-01 hata: 9.58e-01
n=2    1.18056    (bir sonraki terim: 1.93e-01 hata: 6.11e-01
n=10   1.73684    (bir sonraki terim: 1.22e-02 hata: 5.49e-02
n=100  1.79176    (bir sonraki terim: 9.96e-11 hata: 5.71e-10
n=500  1.79176    (bir sonraki terim: 4.27e-43 hata: 2.22e-16

Bu fonksiyonu $L$ fonksiyonu ile aynı programa yazıp, çağırırsanız $n = 1, 2, 10, 50, 100$ terim kullanarak ln(1+x) değerini verilen seriyle hesaplar. (Deneyiniz!)

Python return ifadesi olmayan fonksiyonlara gerçekte $return None$ şeklinde gizli bir ifade ekler. $None$ özel bir Python nesnesidir.

Anahtar Kelime Argümanları

İhtiyacımıza göre fonksiyonlarda anahtar kelimeler kullanıp, bunları her zaman değiştirmek istemediğmiz için varsayılan değerlere atayabiliriz. Böylece fonksiyonu bu değerleri değiştirmeden çağıracağımız vakit bu değişkenleri kullanmak zorunda kalmayız! Tipik bir örnek aşağıdaki gibidir.

In [ ]:
def fonk(arg1, arg2, kwarg1=True, kwarg2=0):
    print(arg1, arg2, kwarg1, kwarg2)
fonk('Merhaba', [1,2])
fonk('Merhaba', [1,2], kwarg1='Selam')
fonk('Merhaba', [1,2], kwarg2='Selam')
fonk('Merhaba', [1,2], kwarg2='Selam', kwarg1=6)

$arg1$,$arg2$ normal, sıradan argümanlarken (positional arguments: konum argümanları), $kwarg1$ ve $kwarg2$, anahtar kelime argümanları (keyword arguments) olarak adlandırılır. Fonksiyon tanımlarında anahtar kelime argümanları, konum argümanlarından sonra yazılmalıdır.

Örnekler 2

Fourier Fonksiyonu ve Harmonik Hareket

Fourrier fonksiyonunu $t$'yi bağımsız değişken yapıp $A$, $a$ ve $\omega$ için uygun varsayılan değerlerin verildiği bir fonksiyonla yazıp, bir yerlerde saklayabiliriz. Astronomide muhakkak işimize yarayacaktır!

$$f(t, A, a, \omega) = A e^{-a t} sin(\omega t)$$
In [ ]:
from math import pi, exp, sin
def f(t, A=1, a=1, omega=2*pi):
    return A*exp(-a*t)*sin(omega*t)

# Bu fonksiyon icin alternatif cagirma ifadeleri
v1 = f(0.2)
v2 = f(0.2, omega=1)
v3 = f(1, A=5, omega=pi, a=pi**2)
v4 = f(A=5, a=2, t=0.01, omega=0.1)
v5 = f(0.2, 0.5, 1, 1)

Belirsiz Sayıda Fonksiyon Argümanı

Fonksiyonlarınızda zaman zaman sayısını bilmediğiniz argüman üzerinde işlem yapma olasılığı oluşabilir. $*args$ ve $**kwargs$ şeklinde iki ayrı yazımla (syntax) belirsiz sayıda argüman fonksiyona geçirilebilir. $*args$ şeklinde, tek bir yıldız (asterisk, *) anahtar kelime argümanı olmayan, belirsiz sayıdaki argümanı bir fonksiyona geçirmek için kullanılırken; iki yıldız (asterisk, **) belirsiz sayıdaki anahtar kelime argümanını bir fonksiyona geçirmek için kullanılır.

1. Belirsiz Sayıda (Anahtar Kelime Argümanı Olmayan) Argüman (ing. Positional Arguments) İçeren Fonksiyonlar

Bir fonksiyona değişken (belirsiz) sayıda, bulunduğu konuma göre değer alan (anahtar kelime argümanı olmayan) argüman geçirilmek istendiğinde bu argümanların toplanacağı demet nesnesinin isminin önüne bir * işareti konulur. Bu demet nesnesi fonksiyona gönderilen, bulundukları konuma göre değer alan ya da anahtar kelime olarak tanımlanmış argümanların dışındaki tüm değerleri bir arada tutar. Fonksiyonun içerisinde ona bu demet değişken üzerinden gönderilen her bir değere demet değişken fonksyonları, dilimleme ya da klasik $for$ ve $while$ döngüleri yoluyla ulaşılablir.

Aşağıda biri tek bir nesne değer ($normarg$), diğeri ise varsayılan birer değeri olmayan ve belirsiz sayıda nesne değerini bir demet nesnesi argümanında ($*args$) tutan argümanlar üzerinden çalışan $test\_args$ isimli bir fonksyon tanımlanmıştır. Fonksiyon öncelikle $normarg$ yerel değişkenine aktarılan nesne değerini ekrana yazdırmakta, sonra da bir $for$ döngüsü içerisinde belirsiz sayıda nesne değerini tutan $*args$ demet nesnesindeki değerleri tek tek ekrana yazdırmaktadır. Program $for$ döngüsü sonrası ekrana çalışmasını döngünün tamamlandığını bildiren bir dizi $"-"$ işareti getirmektedir. Son olarak ise $args$ nesnesinin tüm bu değerleri tutan bir demet nesnesi olduğu bir $print$ ifadesi ile gösterilmektedir. Örnek bu fonksiyon programa herhangi bir değer döndürmemektedir, ancak bu belirsiz sayıda argüman içeren fonksiyonların programa değer döndüremedikleri anlamına da gelmez.

In [5]:
def test_args(normarg, *args):
    print("Normal bir arguman:", normarg)
    # Belirsiz sayida argumanin sirayla alinmasi
    for arg in args:
        print("Diger Argumanlar:", arg)
    print("---------------------------------")
    print(args)
test_args(1, "iki", 3, 4.5,)
#test_args(1)
#test_args(1, ['x','y','z'])
Normal bir arguman: 1
Diger Argumanlar: iki
Diger Argumanlar: 3
Diger Argumanlar: 4.5
---------------------------------
('iki', 3, 4.5)

Aşağıdaki örnekte belirsiz sayıda tam ya da kayan noktalı sayıyı toplayan ve programda çağrıldığı noktaya döndüren bir fonksiyon ($topla$) görülmektedir. Görüldüğü üzere fonksiyon 4 ya da 1 sayı argümanı için çalıştığı gibi hiç argüman gönderilmese dahi hata vermeden çalışmaktadır.

In [7]:
# Belirsiz sayida sayiyi toplayan kucuk bir kod parcasi
def topla(*sayilar):
    toplam = 0
    for sayi in sayilar:
        toplam += sayi
    return toplam

print("Toplam = {:.2f}".format(topla(1.2, 3,5 ,5,1 , 7.6)))
print(topla(2))
print(topla())
Toplam = 22.80
2
0

2. Belirsiz Sayıda Anahtar Kelime Argümanı İçeren (ing. Keyword Arguments) Fonksiyonlar

Bir fonksiyona değişken (belirsiz) sayıda, ancak anahtar kelime argümanı yapısında ve $anahtar = deger$ şeklinde tanımlanan argümanlar geçirilmek istendiğinde bu argümanların toplanacağı sözlük nesnesinin ($dictionaries$) isminin önüne iki $**$ işareti konulur. Bu sözlük nesnesi fonksiyona gönderilen, bulundukları konuma göre değer alan ya da anahtar kelime olarak tanımlanmış argümanların dışındaki tüm değerleri bir arada tutar. Fonksiyonun içerisinde bu sözlük nesnesinin elemanlarına sözlük fonksyonları (henüz sözlük nesnesi verilmemiş olduğu için bu fonksiyonları şu aşamada bilmek zorunda değilsiniz), ya da $sozlukadi[anahtar]$ ifadesiyle ulaşılablir.

Aşağıda biri tek bir nesne değer ($normarg$), diğeri ise tuttuğu belirsiz sayıda nesne değerini bir sözlük nesnesi yapsında saklayan ($**kwargs$) argümanlar üzerinden çalışan $test\_kwargs$ isimli bir fonksyon tanımlanmıştır. Fonksiyon öncelikle $normarg$ yerel değişkenine aktarılan nesne değerini ekrana yazdırmakta, sonra da bir $for$ döngüsü içerisinde, $**kwargs$ sözlük nesnesindeki belirsiz sayıdaki nesnenin anahtarlarını ($key$) ve anahtarlara karışılık gelen değerleri ($value$) tek tek ekrana yazdırmaktadır. Program $for$ döngüsü sonrası ekrana çalışmasını döngünün tamamlandığını bildiren bir dizi $"-"$ işareti getirmektedir. Son olarak ise $kwargs$ nesnesinin tüm bu değerleri tutan bir sözlük nesnesi olduğu bir $print$ ifadesi ile gösterilmektedir. Tıpkı anahtar kelime argümanı olmayan ve belirsiz sayıda argüman içeren $args$ demet değişkenin tanımlanmasında olduğu gibi tanım ifadesinde $**$ karakteri başa gelecek şekilde tanımlanmakta, ancak bu karakterler fonksiyon içerisinde söz konusu sözlük değişkenine ulaşılırken kullanılmamaktadır.

In [6]:
def test_kwargs(normarg, **kwargs):
    print("Normal bir arguman:", normarg)
    for key in kwargs:
        print("Anahtar kelime argumani {:}: {:}".format(key, kwargs[key]))
    print("---------------------------------")
    print(kwargs)
    
test_kwargs(normarg=1, arg2="iki", arg3=3)
Normal bir arguman: 1
Anahtar kelime argumani arg2: iki
Anahtar kelime argumani arg3: 3
---------------------------------
{'arg2': 'iki', 'arg3': 3}

Aşağıdaki örnekte belirsiz sayıda öğrencinin öğrenci numaralarına karşılık ad soyadlarını ekrana yazdıran bir fonksiyon (print_ogrenciler) görülmektedir. Görüldüğü üzere fonksiyona öğrencilerin numaraları (anahtar) ve ad-soyadları (değer) $anahtar = deger$ ($key = value$) yapısında gönderilmektedir. Bu anahtar kelime argümanları için tipik bir yapıdır ve dikey atış probleminde cismin t anındaki konumunu bulan fonksiyonda yerçekim ivmesi de ($g = 9.81$) aynı şekilde tanımlanmıştır. Öğrenci numaralarının önüne "_" işaretinin konmasının tek sebebi değişken isimlerinin sadece sayılardan oluşamaması kuralıdır ve başkaca bir sebebi yoktur. $for$ döngüsü dahilinde $ogrenciler$ sözlük değişkeninde toplanan belirsiz sayıda nesnenin anahtarları (ing. key) $ogrno$ nesnesine aktarılıp basılmaktadır Baştaki "_" karakterini atlamak için yazdırmaya 1. indeksten başlandığına dikkat ediniz. Sonrasında döngünün tamamlandığını bildiren bir dizi "---" işareti ve son olarak ise belirsiz sayıdaki anahtar kelime argümanını tutan $ogrenciler$ sözlük nesnesi bir $print$ ifadesi ile gösterilmektedir.

Dikkat edilmesi gereken hususlardan biri de ister anahtar kelime argümanı olsun, ister olmasın, bir kural olmamakla birlikte, argümanları tutan demet ve sözlük nesnelerini sırasıyla $*args$ ve $**kwargs$ isimlerindeki yer değişkenler olmasıdır. Aşağıdaki örnekte $kwargs$ yerine $ogrenciler$, yukarıdaki $topla$ fonksiyonunda ise $*args$ yerine $sayilar$ degişkenleri bu amaçla kullanılmıştır.

In [9]:
# Derse gelen ogrencilerin adini yazdiran fonksyon
def print_ogrenciler(**ogrenciler):
    
    for ogrno in ogrenciler:
        print("Ogrenci no: {:s}\t Ad Soyad: {:s}"\
              .format(ogrno[1:], ogrenciler[ogrno]))
    print("---------------------------------")
    print(ogrenciler)
    
print_ogrenciler(
    # degisken isimleri sayi olamayacagi icin
    # baslarina _ isareti koaylim
            _11050013 = "Umut Akdemir",
            _12050566 = "Pinar Tunc",
            _15050021 = "Buse Sayar"
        )
{'_11050013': 'Umut Akdemir', '_12050566': 'Pinar Tunc', '_15050021': 'Buse Sayar'}
Ogrenci no: 11050013	 Ad Soyad: Umut Akdemir
Ogrenci no: 12050566	 Ad Soyad: Pinar Tunc
Ogrenci no: 15050021	 Ad Soyad: Buse Sayar

İç Dokümantasyon İfadeleri

Python'da bir fonksiyonun ne yaptığını kodu yazan kişinin kendisine ve o kodu okuyan başkalarına anlatmak için kullandığı standart bir yapı vardır. İç dokümantasyon (ing. Doc String) denen bu ifadeler \"\"\" (üç tırnak) arasına yazılırlar.

In [ ]:
def C2F(C):
    """Santigrad dereceyi (C) Fahrenheit dereceye cevirir."""
    return (9.0/5)*C + 32

def line(x0, y0, x1, y1):
    """
    (x0,y0) ve (x1,y1) noktalarindan gecen bir dogrunun
    denklemini ax + b seklinde ifade ederken kullanilan
    a ve b katsayilarini hesaplar ve dondurur.
    x0, y0: dogru uzerinde bir nokta (her ikisi de float)
    x1, y1: dogru uzerinde diger nokta (her ikisi de float)
    return: y = ax + b dogrusu icin a ve b katsayilari (float)
    """
    a = (y1 - y0)/float(x1 - x0)
    b = y0 - a*x0
    return a, b

Fonksiyonların Fonksiyon Argümanları

Python'da bir fonksiyonun argümanı herhangi bir nesne olabilir (tam sayı, reel sayı, metin, liste, demet hiç farketmez. Bu nedenle bir fonksiyonun fonksiyon argümanı da olabilir ve bu özellikle bazı matematiksel problemlerin nümerik çözümünde oldukça da kullanışlıdır. Örnek olarak bir fonkisyonun ikinci türevinin tanımını ele alalım.

$$f''(x) \approx \frac{f(x - h) - 2f(x) + f(x + h)}{h^2}$$
In [ ]:
def diff2(f, x, h=1E-6):
    r = (f(x-h) - 2*f(x) + f(x+h))/float(h*h)
    return r

def g(t):
    return t**(-6)

t = 1.2
print("t^-6 fonksiyonunun ikinci turevinde {:.1f}".format(t))
d2g = diff2(g, t)
print("g’’({:f}) = {:.1f}".format(t, d2g))

Lambda Fonksiyonları

Python'da oldukça hızlı bir fonksiyon tanımlama yöntemi bulunmaktadır. Lambda fonksiyonu adı verilen bu fonksiyonlar, sınırlı kabiliyetlerine rağmen, basit işleri yapmak üzere çok hızlı (tek satırda) tanımlanabildikeri için oldukça kullanışlıdırlar.

In [ ]:
f = lambda x: x**2 + 4
print(f(2))
def f2(x): 
    # verilen lambda fonksiyonuna ozdes fonksiyon
    return x**2 + 4
print(f2(2))
f = lambda x, y : x + y
print(f(1,1))
F = lambda C: (9./5)*C + 32
print(F(21.0))

Lambda Fonksiyonları ve Eşleştirme: Mapping

Eşleştirme (mapping), filtreleme (filtering) ve indirgeme (reducing) Python 2.x'te de yer alan pratik fonksiyonlardır. Ancak aralarında Python'un yaratıcısı Guido van Rossum'un da yer aldığı bazı Python programcıları, bu fonksiyonlarla gelen fonksiyonalitenin hızlı ve kompakt bir yazımla liste oluşturmayı sağlayan list comprehensions ile de elde edilebileceği nedeniyle kullanımlarını önermemektedirler. AST415 dersinde de öğrenciler bu yapılardan sorumlu tutulmamıştır ve öğrenilmesi takip edenlerin isteğine bırakılmıştır. Ayrıca bu fonksiyonlara özdeş sonuçların hızlı liste oluşturma yöntemleri (list comprehensions) nasıl oluşturulabileceğine ilişkin örnekler bu dersin Alıştırmalar bölümünde yer almaktadır.

Eşleştirme (mapping): Lambda fonksiyonlarının özellikle listelerle birlikte çok efektif bir şekilde kullanımını sağlayan bir yöntemdir. Bu işlem $map$ fonksiyonu ile yapılır. $map$ fonksiyonu bir fonksiyonun bir dizinin bütün elemanları üzerinde uygulanmasını sağlar. Sonuç fonksiyon uygulandıktan sonra oluşan bir dizidir.

In [ ]:
Cderece = list(range(-5,41,5))
Fderece = list(map(lambda x: (float(9)/5)*x + 32, Cderece))
print(Fderece)
a = [1,2,3,4]
b = [17,12,11,10]
c = [-1,-4,5,9]
print(list(map(lambda x,y:x+y, a,b)))
print(list(map(lambda x,y,z:x+y+z, a,b,c)))
print(list(map(lambda x,y,z:x+y-z, a,b,c)))

Lambda Fonksiyonları ve Filtreleme: Filtering

Filtreleme (filtering): Bir fonksiyonun $True$ döndürdüğü değerlere göre bir liste içinden eleme yapmak oldukça “zarif” bir yöntemdir. Bu işlem $filter$ fonksiyonu ile yapılır ve lambda fonksiyonları bu işlemde önemli bir kolaylık sağlarlar.

In [ ]:
fib = [0,1,1,2,3,5,8,13,21,34,55]
ikiye_tam_bolunenler = list(filter(lambda x: x % 2 == 0, fib))
print(ikiye_tam_bolunenler)
ikiye_tam_bolunmeyenler = list(filter(lambda x: x % 2, fib))
print(ikiye_tam_bolunmeyenler)
a = range(-5,5)
negatifler = list(filter(lambda x: x<0, a))
print(negatifler)
pozitifler = list(filter(lambda x: x>0, a))
print(pozitifler)

Lambda Fonksiyonları ve İndirgeme: Reducing

İndirgeme (reducing): Bu fonksiyon Python 3.x ile birlikte herhangi bir paket kullanılmadan çalışan, hazır (built-in) bir fonksiyon olmaktan çıkarılmış ve $functools$ modülüne taşınmıştır. Dolayısı ile kullanılması için bu modülden çağrılması ($import$ edilmesi) gereklidir. $reduce(fonksiyon, dizi)$ fonksiyonu argüman olarak verilen dizi üzerinde sürekli uygular ve tek bir değer döndürür. Dizi argüman $s = [ s1, s2, s3, ... , sn ]$ şeklinde tanımlanmış bir Python listesi olsun. Öncelikle argüman olarak verilen fonksiyon $s1$ ve $s2$ üzerine uygulanır. Fonksiyon bu değerlerden birini seçer. Daha sonra fonksiyon seçilen bu değer ile $s3$ üzerine uygulanır (fonksiyon((fonksiyon(s1,s2),s3)). Bir sonraki adımda bu kez seçilen değer ile $s4$'e uygulanır ve bu işlem verilen listede tek bir eleman kalıncaya kadar devam eder.

In [ ]:
from functools import reduce
gauss_toplam = reduce(lambda x, y: x+y, range(1,101))
print(gauss_toplam)
L = ['Python ', 'programlama ', 'dili ', 'bir ', 'harika ', 'dostum!']
birlesik = reduce(lambda x,y:x+y, L)
print(birlesik)
carpim = reduce(lambda x, y: x * y, [1, 2, 3, 4])
print(carpim)

Şartlı Yapılar: if, elif, else

Aşağıdaki şekilde tanımlanmış bir f(x) parçalı fonksiyonu olsun.

$$ f(x) = sin(x), 0 \le x \le \pi$$$$ f(x) = 0, x \lt 0 , \pi \lt x $$

Bu parçalı fonksiyonu bir Python fonksiyonu olarak yazmak için şartlı ifade yapısı kullanmak gereklidir.

In [ ]:
from math import pi, sin
def f(x):
    if 0 <= x <= pi:
        return sin(x)
    else:
        return 0

Bir başka örnekte şartlı yapıyı, santigrad-fahrenheit derece dönüşümünde fiziksel olmayan sonuçları denetleyip ayıklamakta kullanalım.

In [ ]:
def F(C):
    if C < -273.15:
        print('{:g} derece santigrad fiziksel degildir!'.format(C))
        print('Fahrenheit dereceye donusturme islemi yapilamaz')
    else:
        F = 9.0/5*C + 32
        return F

Bir başka örnekte aşağıdaki parçalı fonksiyon, bir Python fonksiyonu olarak yazılmak isteniyor olsun.

$$ f(x) = 0, x \le 0$$$$ f(x) = x, 0 \le x \lt 1 $$$$ f(x) = 2 - x, 1 \le x \lt 2 $$$$ f(x) = 0, 2 \le x $$

Bu durumda yapılması gereken $if$ ve $else$ yapılarının yanı sıra $elif$ yapısını da kullanmaktır.

In [ ]:
def N(x):
    if x < 0:
        return 0.0
    elif 0 <= x < 1:
        return x
    elif 1 <= x < 2:
        return 2 - x
    elif x >= 2:
        return 0.0

$if$ – $else$ yapısı Python'da (ve diğer tüm programlama dillerinde) çok kullanılan bir yapı olduğu için Python bu yapı için tek satırlık bir ifade sağlamıştır.

In [ ]:
from math import pi, sin
def f(x):
    return (sin(x) if 0 <= x <= pi else 0)
print("sin(pi/2) = ", f(pi/2.))

Alıştırmalar

  1. Sıcaklığı Kelvin cinsinden verilen bir karacismin ışınımının maksimumunun dalgaobyunu cm cinsinden Wien kayma yasası uyarınca hesaplayan ve döndüren bir fonksiyon yazınız.

  2. Görsel parlaklığı (m) kadir cinsinden ve uzaklığı (d) parsek cinsinden verilen bir yıldızın mutlak parlaklığını uzaklık modülünü kullanarak hesaplayan ve döndüren bir fonksiyon yazınız.

  3. Verilen iki sayının eşitliğini belirli bir tolerans değeri çerçevesinde denetleyen, bu iki sayının arasındaki farkın multak değeri verilen tolerans değerinden küçükse $True$, değilse $False$ döndüren bir fonksiyon yazınız. Tolerans değerini bir anahtar kelime argümanı (ing. keyword argument) olarak, varsayılan değeri $10^{-6}$ olacak şekilde tanımlayınız. Fonksiyonunuzu test etmek için 1.0 ile 1 / 49 x 49 işleminin sonucunun eşit olup olmadığını denetlemek üzere varsayılan tolerans değeri ve $10^{-16}$ tolerans değerini kullanınız.

  4. İki sayıyı argüman olarak alıp, toplamlarını döndüren bir normal fonksiyon bir de onun özdeşi $lambda$ fonksiyonu yazınız.

  5. Santigrad dereceyi Fahrenheit'a dönüştüren bir normal fonksiyon bir de özdeşi $lambda$ fonksiyonu yazınız.

  6. İlk hızı $V_{0}$ olan bir cismin dikey atış formülünü kullanarak t anındaki konumunu bulan bir fonksiyon yazınız. Fonksiyonunuzun son argümanı varsayılan değeri 9.81 $m/s^{2}$ olan yerçekimi ivmesi ($g = 9.81$) olmalıdır. $yol$ adını vereceğinizi bu fonksiyona özdeş bir de $lambda$ fonksiyonu yazarak, testini $V_{0} = 5 m/s$, $t = 1 s$ ve $g = 10 m/s^{2}$ için fonksiyonlarınızın hesaplayacağı değeri ekrana yazdırmak suretiyle yapınız.

  7. Aynı uzunlukta üç listenin karşılıklı elemanlarını toplayarak bir listeye atan ve programda çağrıldığı noktaya döndüren bir fonksiyon yazınız. Fonksiyonunuzu $a = [1,2,3,4], b = [17,12,11,10], c = [-1,-4,5,9]$ listeleri üzerinden test ediniz.

  8. $fib = [0,1,1,2,3,5,8,13,21,34,55]$ listesindeki ikiyle bölünebilen sayıları $ikiyle$_$tam$_$bolunenler$ listesinde bölünmeyenleri $ikiyle$_$tam$_$bolunmeyenler$ listesinde toplayınız ve bu listeleri ekrana yazdırınız.

  9. $a = [0.1, 9.0, -4.2, 0.5, -2.3, 3.1, 11.0]$ listesinde -2'den küçük sayıları, -2 ile 2 arasındaki sayıları (-2 ve 2 dahil) ve 2'den büyük sayıları ekrana yazdıran bir program yazınız.

  10. Belirli integral alabilmek için kullanılan yöntemlerden biri de yamuk yöntemidir. $x = a$ ile $x = b$ arasında tanımlı, sürekli ve integrallanebilir bir $f$ fonksiyonunun bu aralıktaki belirli integrali yamuk yöntemiyle aşağıdaki şekilde hesaplanabilir.

$$\int_{a}^{b} f(x) dx \approx \frac{1}{2} \frac{f(a) + f(b)}{b - a}$$

Bu ifadeyle integral hesaplayan ve programda çağrıldığı yere döndüren bir fonksiyon yazınız. Fonksiyonunuzu a) $math$ kütüphanesindeki $sin$ fonksyonunun $0$ ile $\pi / 2$ değerleri arasındaki integralini alarak, b) argüman olarak verilen bir $x$ için $x^3$ 'ü hesaplayıp döndüren kendi yazacağınız bir fonksiyonun $1$ ile $3$ değerleri arasındaki integralini hesaplayarak test ediniz.

  1. Verilen iki aynı tür nesneyi o nesnelerin toplamına ilişkin kurallar dahilinde toplayıp çağrıldığı noktaya döndüren bir fonksyon yazınız. a) Fonksiyonunuzu iki tam, iki kayan sayı, iki liste ve iki metin nesnesi toplayarak test ediniz. b) Fonksiyonunuzu hiçbir sayı göndermeden, sırasıyla tek bir tane ve üç tane sayı göndererek test ediniz. Çalışmıyorsa neden çalşmadığını anlamaya çalışınız.

  2. Bir önceki soruda yazdığınız $toplam$ fonksiyonunu belirsiz sayıda ve anahtar kelime argümanı içermeyen sayıyı (tam ya da kayan noktalı) toplayacak şekilde düzenleyiniz.Fonksiyonunuzu hiçbir sayı göndermeden, sırasıyla tek bir tane, iki tane ve üç tane sayı göndererek test ediniz.

  3. Belirsiz sayıda rengi anahtar kelime ($keyword$), bu renklere karşılık HTML renk kodlarını ise değer ($value$) olarak $anahtar = deger$ şeklinde tanımlanan anahtar elime argümanlarını alarak her renk ve ona karşılık renk kodunu ekrana yazdıran bir fonksiyon yazınız. Fonksiyonunuzu kırmızı, beyaz ve siyah renkleri için test ediniz.

  4. Belirsiz sayıda anahtar kelime argümanları olarak tanımlayacağınız nesneyi alan ve sırasıyla sadece anahtar isimlerini ekrana yazdıran $sabitler$ isimli bir fonksiyon yazınız. Fonksiyonunuzu yer çekimi sabiti ($g$), evrensel çekim sabiti ($G$), Boltzman sabiti ($k$) ve ışık hızı ($c$) ile test ediniz.

  5. Bir önceki soruda yazdığnız fonksiyonu gönderdiğiniz sabitlerin sadece değerlerini ekrana yazdıracak şekilde düzenleyiniz.

  6. Bir önceki soruda yazdığınız fonksiyonu gönderdiğiniz sabitler ve değerlerini birlikte ekrana yazdıracak şekilde düzenleyiniz.

  7. Program tarafında gönderilen bir n tam sayısına kadar (n hariç) tam sayılar içerisinden 5 ile bölünebilenleri ekrana yazdıran (listeye atmanız ya da programa döndürmeniz istenmemektedir) $beslebolunenler$ isimli bir fonksiyon yazınız. n'in pozitif bir sayı olabileceği gibi negatif bir sayı da olabileceğine; n'in pozitif olması durumunda n'e sayacınızı arttırarak, negatif olması durumunda ise azaltarak ulaşabileceğinize dikkat ediniz. Fonksiyonunuzu $n = 102$ ve $n = -102$ için test ediniz.

  8. Kenar uzunlukları verilen bir üçgenin türünü (eşkenar, ikizkenar ya da çeşitkenar) ekrana yazdıran bir fonksiyon yazınız. Fonksiyonunuzu bir eşkenar, bir ikizkenar, bir de çeşitkenar üçgen için test ediniz.

  9. "Eratosthenes'in eleği" 2'den bir N pozitif tam sayısına kadar (N dahil) tüm asal sayıları veren bir algoritmadır. Verilen bir N pozitif tam sayısı için Eratosthenes'in eleği algoritması ile belirlediği N'den küçük veya eşit tüm asal sayıları bir liste içerisine toplayan ve programda çağrıldığı yere döndüren bir Python fonksiyonu yazınız. Fonksiyonunuzu N = 100 için test ediniz.

  10. Aşağıda bazı yıldızlar, parsek (pc) cinsinden uzaklıkları ve gezegen içerip içermediklerine iişkin Boolean bir değerden oluşan demet nesnelerini içeren bir Python listesi içerisinde verilmiştir. Bu yıldızlardan gezegen barındıran ve varsayılan değeri 5 pc'lik bir uzaklıktan yakın olanlarını ekrana yazdıran bir fonksiyon yazınız. Fonksiyonunuzu bu varsayılan uzaklık değeri için ve bu değeri 3 ve 20 pc olacak şekilde değiştirerek test ediniz.

veri = [ ('Proxima', 1.30, True), ('Alfa Cen A', 1.32, False), ('Alfa Cen B', 1.32, False), ('Barnard Yildizi', 1.83, True), ('Wolf 359', 2.59, False), ('Wolf 940', 12.39, True), ('Wolf 503', 42.68, True), ('Wolf 1061', 4.31, True), ('Ross 128', 3.31, True), ('Lalande 21185', 2.55, False), ('GJ 3779', 13.75, True), ('Sirius', 2.64, False), ('Ross 154', 2.98, False), ('Epsilon Eridani', 3.22, True), ('EZ Aqr', 3.40, False), ('Procyon', 3.52, False), ('Tau Ceti', 3.65, True), ('Gliese 832', 4.96, False), ('HD 2685', 198.2, True), ('K2 136', 59.4, True), ('HD 219134', 6.55, True), ('Vega', 7.68, False) ]