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
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.
Ö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.
def F(C):
fahr = (9.0/5)*C + 32
return fahr
F(11)
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.
Bir fonksiyonu kullanmak ve ondan değer alabilmek için o fonksiyonu “çağırmanız" gerekir. Tipik birkaç örnek aşağıda verilmiştir.
santigrad = 10
fahrenheit = F(santigrad) # zira santigrad 10 olarak tanimlanmis
print(fahrenheit)
print(F(santigrad+10.0))
toplam_sicaklik = F(10.0) + F(20.0)
print(toplam_sicaklik)
Cderece = range(-5,41,5)
Fderece = [F(santigrad) for santigrad in Cderece]
print(Fderece)
İstenildiği takdirde fonksiyon formatlı bir metin döndürecek şekilde yazılabilir, fonksiyonun içinde de nesneler tanımlanabilir.
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)
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.
santigrad = 21.0
metin = F2(santigrad)
print(metin)
print(fahr)
# sonra bunu deneyin!
#print(sant)
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.
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.
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)
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.
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.
print(f1(b))
print("a = ", a)
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!
Ş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.
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))
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.
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.
Ş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.
def fonk(t, v0):
g = 9.81
y = v0*t - 0.5*g*t**2
dydt = v0 - g*t
return y, dydt
# Coklu nesne donduren fonksiyonlardan dondurulen bir demet degiskendir
demet = fonk(0.6, 6)
print(demet)
konum = demet[0]
hiz = demet[1]
print("Cismin t={:.1f} saniyedeki konumu {:.2f}, hizi {:.2f} tir".format(0.6, konum, hiz))
# Bu fonksiyondan deger dondururken iki degiskene ihtiyac duyariz
konum, hiz = fonk(0.5,6)
print("Cismin t={:.1f} saniyedeki konumu {:.2f}, hizi {:.2f} tir".format(0.5, konum, hiz))
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.
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.
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 = abs(log(1 +x) - toplam)
return toplam, ihmal_edilen_ilk_terim, hata
# n = 100 ve x = 5, icin ln(1+x) = ln(6)
sonuc, ihmal, tam_hata = L(x = 5, n = 100)
print("Sonuc: {:.2f}, ihmal: {:.16f}, hatasi: {:.16f} tir"\
.format(sonuc, ihmal, tam_hata))
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.
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)
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.
İ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.
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.
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)$$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)
print(v1,v2,v3,v4,v5)
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.
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.
def test_args(normarg, *args):
na = normarg
print("Normal bir arguman:", na)
# Belirsiz sayida argumanin sirayla alinmasi
for arg in args:
print("Diger Argumanlar:", arg)
print("---------------------------------")
print(args)
test_args(1, "iki", 3, 4.5,"AST15",(2,3,5))
#test_args(1)
#test_args(1, ['x','y','z'])
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.
# 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())
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.
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)
N = 1
test_kwargs(normarg=N, arguman2="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.
# 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"
)
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.
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
print(C2F)
help(C2F)
help(line)
demet = line(1,2,3,7)
egim = demet[0]
kesme = demet[1]
print("m = {:g}, n = {:g}".format(egim,kesme))
x_0 = 1; x_1 = 3; y_0 = 2; y_1 = 7
m,n = line(x0 = x_0,x1 = x_1, y0 = y_0, y1 = y_1)
print("{:} x + {:}".format(m,n))
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}$$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, h=1e-7)
print("g’’({:f}) = {:.1f}".format(t, d2g))
print("{:.1f}".format(42*t**(-8)))
def f(x):
from math import log
return 2*log(5*x)
print("f''(x = 3) = {:.2f}".format(diff2(f, x=3)))
from math import pi,sin
print("sin''(x = pi/2) = ", diff2(sin, pi/2))
from math import e,log
print("ln''(x = e^2) = ", diff2(log,e**2))
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.
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))
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.
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)))
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.
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)
İ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.
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)
Bir fonksiyonu kendi içinden çağırarak istediğiniz tekrar sayısında bir işlemi tekrarlamanız mümkün olabliir. Bu amaca yönelik olarak kullanılan fonksiyonlara programcılıkta yinelemeli (recursive) fonksiyonlar denir. Özünde map
, filter
ve functools.reduce
fonksiyonları ile yapılan işlem de birer çeşit yinelemedir. İyi bir örnek filter
fonksiyonuna örnek olarak gördüğünüz Fibonacci sayı dizisini istediğiniz uzunlukta oluşturmak olabilir ve aşağıdaki kod bloğuyla istenen gerçekleştirilebilir.
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n - 1) + fibonacci(n - 2)
N = 15
print('{:d} uzunlugundaki Fibonacci dizisi:'.format(N))
fib = []
for i in range(N):
fib.append(fibonacci(i))
print(fib)
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.
from math import pi, sin
def f(x):
if 0 <= x <= pi:
return sin(x)
else:
return 0
f(3*pi/4)
f(-pi/2)
Bir başka örnekte şartlı yapıyı, santigrad-fahrenheit derece dönüşümünde fiziksel olmayan sonuçları denetleyip ayıklamakta kullanalım.
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 \lt 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.
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.
from math import pi, sin
def f(x):
return (sin(x) if 0 <= x <= pi else 0)
print("sin(pi/2) = ", f(pi/2.))
Zaman zaman Python döngülerinden belirli bir koşula bağlı olarak çıkmak ya da başka işlemler yaparak devam etmek gerekebilir. Bu durumlar için Python break
, continue
ve else
gibi özel ifadelere sahiptir.
Python'daki break
ifadesi, bir döngüyü hemen sonlandırır ve program döngü gövdesini izleyen ilk ifadeyle ilerler.
x = 6
while x > 0:
x -= 1
if x == 2:
break
print("x = ", x)
print('Dongunun sonu!')
continue
ifadesi ise bir şarta bağlı olarak döngünün sadece o şart için çalışmamasını ve döngünün en başa dönmesini sağlar. Yukarıdaki örnekte break
ifadesi yerine continue
ifadesi konduğunda bu kez de döngü verilen koşul için çalışmaz ancak döngüden çıkılması yerine başına dönülür.
x = 6
while x > 0:
x -= 1
if x == 2:
continue
print("x = ", x)
print('Dongunun sonu!')
Görüldüğü üzere $x = 2$ için döngü bir sonraki satırdan devam etmek yerine başa dönmüş, x 1 azaltılarak ($x -= 1$) 1 değerini almış ve bu kez $x == 2$ koşulu sağlanmadığı için döngü bir satır sonraki print ifadesi ile devam etmiştir. Bu nedenle x için 2 değeri ekrana gelmezken, x'in diğer değerleri yazdırılabilmiştir.
Python, bir while
döngüsünün sonunda isteğe bağlı bir else
ifadesinin kullanılmasına izin verir.Python'un diğer birçok programlama dilinde bulunmayan bu benzersiz özelliği tıpkı şartlı yapılarda olduğu gibi while
döngüsünün çalışmasını sağlayan koşulun tersi (değili) gerçekleştiğinde ilave bir kod bloğunun çalışmasını sağlar.
x = 6
while x > 0:
x -= 1
print("x = ", x)
else:
print("-1'i sevmiyoruz!")
print('Dongunun sonu!')
Burada dikkat edilmesi gereken önemli bir nokta else
ifadesinin while
döngüsünün bir tamamlayıcı parçası olduğudur. Bu nedenle while
döngüsünden örneğin break
ifadesi ile çıkıldığında else
bloğu da çalışmaz.
İçiçe döngülerde kullanılan break
ifadesi ise kullanıldığı döngüden çıkılmasını sağlarken bir üstteki döngünün ifadeleriyle devam edilmesine engel olmaz.
x = 6
while x > 0:
x -= 1
if x == 2:
break
print("x = ", x)
else:
print("+2'yi sevmiyoruz!")
print('Dongunun sonu!')
Görüldüğü gibi else
bloğundaki print ifadesi döngüden çıkıldığı için çalıştırılamamıştır. Döngünün koşulu ($x > 0$ iken) sağlandığı sürece çalışan bir if
ifadesi, else
bloğunu ise koşul yanlış ($False$) olduğu ve döngünün bu koşula bağlı olarak artık çalışmayı bıraktığı durumda çalışan bir ifadeler bloğu olarak düşünmekte fayda vardır.
Her ne kadar bir değeri bilinen bir nesneyi bir liste içerisinde aramanın çok daha efektif yöntemleri varsa da else
bloğunun bir kullanımı bir liste elemanı için tüm listeyi taramak üzere yazılmış aşağıdaki kod bloğuyla örneklenebilir.
yildizlar = ['sirius', 'vega', 'deneb', 'betelgeuse']
yildiz = "vega"
i = 0
while i < len(yildizlar):
if yildizlar[i] == yildiz:
print('Aranan yildiz ({:s}) bulundu!'.format(yildiz))
break
i += 1
else:
# Aranan yildiz bulunamadi!
print(yildiz, 'yildizlar listesi icinde bulunamadi.')
Listeleri taramak için bir listenin eleman sayısı bilindiğinden $for$ döngüleri çok daha efektiftir. Ayrıca liste elemanı bulmak üzere $in$ yapısı kullanmak da bir döngü kullanmaya göre çok daha hızlı sonuç verir.
yildiz = 'vega'
if yildiz in yildizlar:
print(yildiz, 'yildizlar listesinde bulundu')
else:
print(yildiz, 'yildizlar listesi icinde bulunamadi.')
Alternatif bir çözüm $yildizlar$ listesindeki $yildiz$ adlı yıldızın sayısını listeler üzerinde tanımlı count
metoduyla almak olabilir.
if yildizlar.count(yildiz):
print(yildiz, 'yildizlar listesinde bulundu')
else:
print(yildiz, 'yildizlar listesi icinde bulunamadi.')
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.
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.
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.
İ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.
Santigrad dereceyi Fahrenheit'a dönüştüren bir normal fonksiyon bir de özdeşi $lambda$ fonksiyonu yazınız.
İ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.
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.
$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.
$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.
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.
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.
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.
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.
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.
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.
Bir önceki soruda yazdığnız fonksiyonu gönderdiğiniz sabitlerin sadece değerlerini ekrana yazdıracak şekilde düzenleyiniz.
Bir önceki soruda yazdığınız fonksiyonu gönderdiğiniz sabitler ve değerlerini birlikte ekrana yazdıracak şekilde düzenleyiniz.
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.
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.
"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.
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) ]