Bölüm 5 Fonksiyonlar

Kazanımlar

Fonksiyonların görevi, karmaşık işlemleri bir araya toplayarak, bu işlemleri tek adımda yapmamızı sağlamaktır. Fonksiyonlar çoğu zaman, yapmak istediğimiz işlemler için bir şablon vazifesi görür. Fonksiyonları kullanarak, bir veya birkaç adımdan oluşan işlemleri tek bir isim altında toplayabiliriz. Fonksiyonlar rutin olarak tekrar edilen görevleri veya prosedürleri tek bir ad/çatı altında toplayan araçlardır. R programlama dilinde fonksiyonlar fonksiyon.adi = function(parametreler){işlemler} kuralı ile oluşturulurlar. Hiç parametre içermeyebilirler. Örneğin ekrana “merhaba” yazdıran fonksiyon aşağıdaki kodda verilmiştir.

merhaba = function(){
  print("merhaba")
}

# oluşturulan fonksiyonu kullanabilmek için fonksiyoun 
# adından sonra parantez ve varsa parametreler eklenir 
merhaba()
## [1] "merhaba"

# kullanıcı adını parametre olarak alan ve 
# merhaka kullanıcı adi şeklinde çıktı veren fonksiyon
merhaba2 = function(kullanici.adi){
  print(paste("merhaba", kullanici.adi))
}

merhaba2("Harun")
## [1] "merhaba Harun"

merhaba.ogrenci = function(ogrenci.adi, ogrenci.no){
  print(paste("merhaba", ogrenci.no, ogrenci.adi))
}

merhaba.ogrenci("Recep", "15")
## [1] "merhaba 15 Recep"

Soru1: Girilen sayının asal olup olmadığını söyleyen fonksiyonu yazınız.

Cevap:

asal.mi = function(x){
    sonuc = T
    for (s in 2:(x-1)){
        if (x %% s == 0){ 
            sonuc = F
            # hangi sayıya bölündüğünü görmek için
            # sonraki yorum satırını aktifleştirin
            # print(s)
            break
        }
    }
    print(sonuc)
}

asal.mi(7)
## [1] TRUE
asal.mi(21)
## [1] FALSE
asal.mi(4321)
## [1] FALSE

Soru2: Mutlak değer hesaplayan fonksiyon yazınız.

Cevap:

mutlak.deger = function(x){
    sonuc = x
    if(x < 0){
        sonuc = x * -1
    }
    return(sonuc)
}

mutlak.deger(-8)
## [1] 8
mutlak.deger(0)
## [1] 0
mutlak.deger(4)
## [1] 4

2. Yol: return fonksiyonu kullanılmasa da fonksiyonun son satırındaki ifade fonksiyonun dönüş değeridir.

mutlak.deger = function(x){
    sonuc = x
    if(x < 0){
        sonuc = x * -1
    }
    sonuc
}

mutlak.deger(-8)
## [1] 8
mutlak.deger(0)
## [1] 0
mutlak.deger(4)
## [1] 4

Örnek: Basit Hesapmakinesi döngülü (repeat ile) örneğini daha kullanışlı hale getirmek için hatalı giriş yapıldığında da işlem numaraları hatırlatılabilir. Bunu doğrudan print ile alt alta ekleyebilsek de kod uzayacağından anlaşılması zorlaşır. Bu durumda fonksiyonlardan faydalanabiliriz.

Fonksiyon ile desteklenmeyen kod

print("* Hesap Makinesi *")
print("Hesap makinesinde işlem numaraları aşağıdaki gibidir.")
print("0: çıkış")
print("1: Toplama")
print("2: Çıkarma")
print("3: çarpma")
print("4: Bölme")


repeat{ 
  islem = readline("İşlem numarasını giriniz: ")
  
  if (islem == "0"){
    print("çıkılıyor...")
    break
  }
  
  if (!islem %in% 0:4){
    print("Hatalı işlem numarası. Lütfen yeniden deneyin.")
    print("Hesap makinesinde işlem numaraları aşağıdaki gibidir.")
    print("0: çıkış")
    print("1: Toplama")
    print("2: Çıkarma")
    print("3: çarpma")
    print("4: Bölme")
    next
  }
  # readline ile alınan değerler karakter olduğundan 
  # sayısal işlemlere tabi tutamayız bu yüzden önce 
  # girilen ifadenin sayıya dönüştürülmesi lazım 
  # bunun için as.numeric() fonksiyonu kullanıldı
  sayi1 = as.numeric(readline("İlk sayıyı giriniz: "))
  sayi2 = as.numeric(readline("İkinci sayıyı giriniz: "))
  
  if(islem == "1"){
      print(paste(sayi1, "+", sayi2, "=", sayi1 + sayi2))
  }else if(islem == "2"){
      print(paste(sayi1, "-", sayi2, "=", sayi1 - sayi2))
  }else if(islem == "3"){
      print(paste(sayi1, "*", sayi2, "=", sayi1 * sayi2))
  }else if(islem == "4"){
      print(paste(sayi1, "/", sayi2, "=", sayi1 / sayi2))
  }else{
      "hatalı işlem numarası girildiğinden işlem yapamadık"
  }
}

fonksiyon ile desteklenen kod

bilgi = function(){
  print("* Hesap Makinesi *")
  print("Hesap makinesinde işlem numaraları aşağıdaki gibidir.")
  print("0: çıkış")
  print("1: Toplama")
  print("2: Çıkarma")
  print("3: çarpma")
  print("4: Bölme")
}

bilgi()
repeat{ 
  islem = readline("İşlem numarasını giriniz: ")
  
  if (islem == "0"){
    print("çıkılıyor...")
    break
  }
  
  if (!islem %in% 0:4){
    bilgi()
    next
  }
  # readline ile alınan değerler karakter olduğundan 
  # sayısal işlemlere tabi tutamayız bu yüzden önce 
  # girilen ifadenin sayıya dönüştürülmesi lazım 
  # bunun için as.numeric() fonksiyonu kullanıldı
  sayi1 = as.numeric(readline("İlk sayıyı giriniz: "))
  sayi2 = as.numeric(readline("İkinci sayıyı giriniz: "))
  
  if(islem == "1"){
      print(paste(sayi1, "+", sayi2, "=", sayi1 + sayi2))
  }else if(islem == "2"){
      print(paste(sayi1, "-", sayi2, "=", sayi1 - sayi2))
  }else if(islem == "3"){
      print(paste(sayi1, "*", sayi2, "=", sayi1 * sayi2))
  }else if(islem == "4"){
      print(paste(sayi1, "/", sayi2, "=", sayi1 / sayi2))
  }else{
      "hatalı işlem numarası girildiğinden işlem yapamadık"
  }
}

Örnek: Karakter verisindeki Türkçe karakterleri değiştiren fonksiyon.

# Türkçe karakterleri değiştirir
degis <- function(x, bosluk="", df=NULL){
    x <- gsub("ç", "c", x)
    x <- gsub("Ç", "C", x)
    x <- gsub("ğ", "g", x)
    x <- gsub("Ğ", "G", x)
    x <- gsub("ı", "i", x)
    x <- gsub("İ", "I", x)
    x <- gsub("ö", "o", x)
    x <- gsub("Ö", "O", x)
    x <- gsub("ş", "s", x)
    x <- gsub("Ş", "S", x)
    x <- gsub("ü", "u", x)
    x <- gsub("Ü", "U", x)
    x <- gsub(" ", bosluk, x)

    if(!is.null(df)){
        for(i in 1:nrow(df)){
            x <- gsub(df[i, 1], df[i, 2], x)
        }
    }

    return(x)
}

degis("Türkçe karakterleri değiştiren fonksiyon", bosluk = "_")
## [1] "Turkce_karakterleri_degistiren_fonksiyon"

5.1 Parametreler

Fonksiyonda kullanılmak üzere oluşturulan ve genelde değerlerini dışarından alan fonksiyon değişkenleridir. Örneğin yukarıda tanımlaman degis isimli fonksiyondaki x, bosluk ve df isimleriyle belirtilen değişkenler parametrelerdir. x parametresinin dışarıdan girilmesi zorunlu iken diğer parametre değerlerinin dışarıdan girilmesine gerek yoktur.

# bosluk ve df parametrelerinin öntanımlı değerleri 
# olduğundan dışarıdan veri girilmesi gerekmez
degis("Türkçe karakterleri değiştiren fonksiyon")
## [1] "Turkcekarakterleridegistirenfonksiyon"

# ama x değişkeni için bir tanımlama yapılmadığından
# x değişkeni girilmexse program hata verecektir
degis(bosluk="_")
## Error in gsub("ç", "c", x): varsayılanı olmayan "x" argümanı yok

Fonksiyon parametreler ile tanımlanıp kullanılmak istenirse aşağıdaki başlıklarda belirtilen şekilde kullanılabilir.

5.2 Sıralı (veya İsimsiz) Parametreler

Belirtilen parametre isimleri kullanılmadan sırayla argümanlar verilerek kullanılabilir.

ogrenci.kayit = function(isim, soyisim, numara, sinif, sube){
    print(strrep("-", 30))

    print(paste("isim           : ", isim))
    print(paste("soyisim        : ", soyisim))
    print(paste("numamra        : ", numara))
    print(paste("sınıf          : ", paste0(sinif, "-", sube)))

    print(strrep("-", 30))
}

# parametre isimleri belirtilmeden kullanılacaksa
# değerler sırasıyla yazılmalı
ogrenci.kayit("Veli", "Deli", 1234, "9", "A")
## [1] "------------------------------"
## [1] "isim           :  Veli"
## [1] "soyisim        :  Deli"
## [1] "numamra        :  1234"
## [1] "sınıf          :  9-A"
## [1] "------------------------------"

5.3 İsimli Parametreler

Fonksiyon kullanılırken parametre isimleri belirtildiğinde sıralama gerekli değildir.

# Eğer fonksiyon kullanılırken parametre isimleri belirtilirse 
# fonksiyon tanımlanırken belirtilen parametre sırası önemini yitirir.
merhaba.ogrenci(ogrenci.no = 15, ogrenci.adi = "Recep")
## [1] "merhaba 15 Recep"

# isimleri belirtilen parametreler için sıra önemli değildir
ogrenci.kayit(num = 1234, isim = "Veli", sinif = "9", soyisim = "Deli", sube = "A")
## [1] "------------------------------"
## [1] "isim           :  Veli"
## [1] "soyisim        :  Deli"
## [1] "numamra        :  1234"
## [1] "sınıf          :  9-A"
## [1] "------------------------------"

Aynı anda hem isimli hem isimsiz parametre kullanılabilir bu durumda isimsiz parametrelerin yine kendi aralarında sırayla yazılması gerekir. Aksi takdirde bu durumda fonksiyon hatalı çalışacaktır.

# aynı anda hem isimli hem isimsiz parametre kullanılabilir bu durumda 
# isimsiz parametrelerin yine kendi aralarında sırayla yazılması gerekir 
ogrenci.kayit(numara = 1234, isim = "Veli", sinif = "9", "A",  "Deli")
## [1] "------------------------------"
## [1] "isim           :  Veli"
## [1] "soyisim        :  A"
## [1] "numamra        :  1234"
## [1] "sınıf          :  9-Deli"
## [1] "------------------------------"
ogrenci.kayit(numara = 1234, isim = "Veli", "A",  "Deli", sinif = "9")
## [1] "------------------------------"
## [1] "isim           :  Veli"
## [1] "soyisim        :  A"
## [1] "numamra        :  1234"
## [1] "sınıf          :  9-Deli"
## [1] "------------------------------"

Bunlara ek olarak parametre isimler diğer parametre isimleriyle çakışmayacak şekilde kısaltılabilir.

# aynı anda hem isimli hem isimsiz parametre kullanılabilir bu durumda 
# isimsiz parametrelerin yine kendi aralarında sırayla yazılması gerekir 
ogrenci.kayit(num = 1234, isim = "Veli", sin = "9",  "Deli", "A")
## [1] "------------------------------"
## [1] "isim           :  Veli"
## [1] "soyisim        :  Deli"
## [1] "numamra        :  1234"
## [1] "sınıf          :  9-A"
## [1] "------------------------------"

5.4 Varsayılan Değerli Parametreler

Fonksiyon tanımlanırken parametrelere varsayılan değerler verebiliriz. Örneğin;

# Türkçe karakterleri değiştirir
degis <- function(x, bosluk="_", df=NULL){
    x <- gsub("ç", "c", x)
    x <- gsub("Ç", "C", x)
    x <- gsub("ğ", "g", x)
    x <- gsub("Ğ", "G", x)
    x <- gsub("ı", "i", x)
    x <- gsub("İ", "I", x)
    x <- gsub("ö", "o", x)
    x <- gsub("Ö", "O", x)
    x <- gsub("ş", "s", x)
    x <- gsub("Ş", "S", x)
    x <- gsub("ü", "u", x)
    x <- gsub("Ü", "U", x)
    x <- gsub(" ", bosluk, x)

    if(!is.null(df)){
        for(i in 1:nrow(df)){
            x <- gsub(df[i, 1], df[i, 2], x)
        }
    }

    return(x)
}

degis("Türkçe karakterleri değiştiren fonksiyon")
## [1] "Turkce_karakterleri_degistiren_fonksiyon"
degis("Türkçe karakterleri değiştiren fonksiyon", bosluk = "/")
## [1] "Turkce/karakterleri/degistiren/fonksiyon"

5.5 Rastgele Sayıda isimli/İsimsiz Parametre Belirleme

R porgramlama dilinde fonksiyon tanımlarken rastgele sayıda parametre alınabilir. Bunun için ... ifadesinden yararlanılır. ... ifadesini kullanabilmek için vektör veya liste olarak almalıyız. örneğin python programlama dilindeki print benzeri bir fonksiyon yazmak istersek Aşağıdaki gibi yazabiliriz.

*** isimsiz parametre alma***

yaz = function(...){
    # `...` değerini kullanabilmek için
    # c fonksiyonuyla vektör olarak değerleri
    # bir değişkene (burada ext.args) aktarıldı
    ext.args = c(...)
    print(paste(ext.args, collapse = " "))
}

yaz("İTÜ", "MTAL", 1:5, "9. sınıf")
## [1] "İTÜ MTAL 1 2 3 4 5 9. sınıf"
yaz = function(...){
    ext.args = list(...)
    
    a = if ("a" %in% names(ext.args)) ext.args$a else "değer girilmedi"
    print(a)
}

yaz(a="İTÜ", b="MTAL", c=1:5)
## [1] "İTÜ"

5.6 return Fonksiyonu

return fonksiyonu, fonksiyonda yapılan işlemler sonucunda elde edilen veriyi dışarı aktarmak için kullanılır. Örneğin;

topla = function(x, y){
    return(x + y)
}

a = topla(10, 15)
print(a)
## [1] 25

Ya da fonksiyon sonunda doğrudan değeri eklemekde aynı sonuca ulaştırır. Ama her zaman fonksiyon sonunda değer aktarması yapılmayacağından dığer durumlarda return fonksiyonu kullanılmalıdır.

topla = function(x, y){
    x + y
}

a = topla(10, 15)
print(a)
## [1] 25
# fonksiyon sonundan önce değer aktarılıp fonksiyon sonlandırılacaksa
# return fonksiyonu kullanılmak zorundadır. Örneğin yukarıdaki "topla"
# fonksiyonuna sayısal değer hariç bir değer girilirse hataya sebep olacaktır.
# bu durumda değerlerin sayısal değer olup olmadığı kontrol edilerek de 
# değer aktarılabilir
topla = function(x, y){
    if(!is.numeric(x) | !is.numeric(y)){
        return(NULL)
    }
    x + y
}

s1 = topla("10", 15)
print(s1)
## NULL

s2 = topla(10, 15)
print(s2)
## [1] 25

5.7 İç İçe Fonksiyonlar

Bir fonksiyon tanımlanırken fonksiyon bloğunda başka bir fonksiyon tanımlanıp fonksiyon içinde kullanılabilir.

fonksiyon1 = function(x){
    ekle = function(x){
        x + 1
    }
    
    while(T){
        if(x==3) break
        print(x)
        x = ekle(x)
    }
    
}

fonksiyon1(1)
## [1] 1
## [1] 2

5.8 Özyinelemeli (Recursive) Fonksiyonlar

Fonksiyonlar başka fonksiyonları çağırabildiği gibi kendi kendilerini de çağırabilirler. İşte bu tür fonksiyonlara ‘kendi kendilerini yineleyen’, veya daha teknik bir dille ifade etmek gerekirse ‘özyinelemeli’ (recursive) fonksiyonlar adı verilir.

geri_sayim = function(x){
    if(x == 0) return(0)
    print(x)
    # 
    # Sys.sleep(1)
    geri_sayim(x - 1)
}

geri_sayim(10)
## [1] 10
## [1] 9
## [1] 8
## [1] 7
## [1] 6
## [1] 5
## [1] 4
## [1] 3
## [1] 2
## [1] 1
## [1] 0

Soru: Faktoryel hesaplayan fonksiyon yazınız.Faktoryel fonksiyonu aldığı Doğal Sayı değeri kendinden küçük sıfırdan büyük olan doğal sayılarla çarpan bir fonksiyondur. Örneğin 3! (3 faktoryel) = 3.2.1 = 6 dır. Not: sıfır faktoryel 1 dir

Cevap:

fkt = function(x){
    if(x == 0) return(1)
    
    return(x * fkt(x - 1))
}

fkt(3)
## [1] 6

Gömülü Fonksiyonlar

Programlama dillerinde halihazırda tanımlı fonksiyonlar bulunur bu fonksiyonlara gömülü fonksiyonlar denir. Örneğin sürekli kullandığımız print() ve paste() fonksiyonları bu dile ait gömülü fonksiyonlardır. Aşağıda çeşitli vektör sınıflarına ait gömülü fonksiyonlar gösterilmiştir.

Sayısal vektör sınıfları için gömülü fonksiyonlar

TABLO

Fonksiyon Tanım
abs(x) mutlak değer bulur
sqrt(x) karekök bulur
ceiling(x) yukarı yuvarlama. ceiling(3.475) = 4
floor(x) aşağı yuvarlama. floor(3.475) = 3
trunc(x)

girilen sayının tamsayı kısmını verir.

trunc(5.99) = 5

trunc(-5.1) = -5

round(x, digits=n) yuvarlama. round(3.475, digits=2) = 3.48
signif(x, digits=n) girilen basamak numarasına göre sayı yuvarlanır. signif(3.475, digits=2) = 3.5
cos(x), sin(x), tan(x) , acos(x), … trigonometrik fonksiyonlar
log(x) doğal logaritma
log10(x) common logarithm

Karakter Fonksiyonları

Fonksiyon Tanım
substr(x, start=n1, stop=n2) Bir karakter vektöründeki alt dizeleri ayıklar veya değiştirir.
x <- “abcdef”
substr(x, 2, 4) alt dizisi “bcd” dir.
substr(x, 2, 4) <- “22222” şeklinde “a222ef”
strsplit(x, split) split ile belirtilen ifadeye göre x ile belirtilen karakter parçalarına ayrılır.
strsplit(“abc”, "“) returns 3 element vector”a“,”b“,”c"
paste(…, sep="") karakteri birleştirmek için kullanılır
paste(“x”,1:3,sep="“) returns c(”x1“,”x2" “x3”)
paste(“x”,1:3,sep=“M”) returns c(“xM1”,“xM2” “xM3”)
paste(“Today is”, date())
toupper(x) karakterleri büyük harfe çevirir. Türkçe karakterler için uygun dönüşüm yapılamayabilir.
tolower(x) karakterleri küçük harfe çevirir. Türkçe karakterler için uygun dönüşüm yapılamayabilir.
strrep(x, times) x ile belirtilen karakterin tekrar etmesini sağlar

İstastistiksel fonksiyonlar

Function Description
mean(x, trim=0, na.rm=FALSE) x nesnesne ait ortalamayı bulur
# trimmed mean, removing any missing values and
# 5 percent of highest and lowest scores
mx <- mean(x,trim=.05,na.rm=TRUE)
sd(x) x nesnesine ait standart sapmayı bulur
median(x) median
range(x) minimum ve maksimum bulur
sum(x) toplam
diff(x, lag=1) lagged differences, with lag indicating which lag to use
min(x) minimum
max(x) maksimum
scale(x, center=TRUE, scale=TRUE) column center or standardize a matrix.

Yukarıda R programlama dilinde dahili olarak gelen bazı fonksiyonlar gösterilmiştir. Bunların dışında hali hazırda paket olarak gelen fonsiyonlar, sınıflar, … da vardır. Ek olarak bağımsız geliştiriciler tarafından sunulan paketler de vardır ve indirlip kurularak kullanıma hazır hale getirilebilirler. Bu paketlere ulaşmak için library fonksiyonundan yararlanılır. Örneğin çeşitli tarih fonksiyonlarını barındıran lubridate adlı paketi R programımıza dahil etmek için aşağıdaki kodu kullanmamız gerekir.


library(lubridate)

Eğer dışarıdan paket kurmak istersek install.packages fonksiyonunu kullanmamız gerekir. Örneğin ggplot2 isimli grafik oluşturma paketini yüklemek için aşağıdaki kodu kullanabiliriz.


install.packages("ggplot2")

Eğer daha önce yazdığımız bir R dosyasındaki fonksiyonlara erişmek istersek source fonksiyonundan yararlanabiliriz:


# daha önce oluşturduğumuz fonksiyon dosyamız
# D:/betik/fonksiyon.R adresinde ise
source("D:/betik/fonksiyon.R", encoding = "utf-8")

# encoding ile belirtilen parametre dosyanın 
# hangi kodlama sistemiyle yazıldığını belitir
# windows için varsayılan kodlama sistemi cp1254 dür
# linux için ise utf-8 dir. 
#
# istenirse Rstudio ayarlarından bu kayıt türü 
# istenilen şekilde değiştirilebilir

Sorular

  1. Dikdörtgen alanı hesaplayan fonksiyonu yazınız.

  2. Ekrana ilk olarak “Yıldız üçgen çiziliyor :” mesajını getiren, ardından her satıra bir döngü ile “*” işareti koyarak üçgen çizen fonksiyonu yazınız.

  3. Ekrana çarpım tablosunu yazan fonksiyonu yazınız.

  4. 2 sınav ve bir sözlü notunu parametre olarak alan ve ortalamayı bulup 50 ve üstü notlar için “Geçti” , tersi için kaldı yanıtı veren fonksiyonu yazınız.

  5. Girilen sayının tam bölünlerini bulup vektör olarak gösteren fonksiyonu yazınız.

  6. Fibonacci dizisinin ilk 20 elemanını yazdıran bir özyinelemeli fonksiyonuyazınız.

Cevaplar

dikdortgen.alan = function(a, b){
    return(a * b)
}

dikdortgen.alan(4, 5)
## [1] 20
yildiz.ciz = function(x){
    print("Yıldız çizdiriliyor")
    for (i in 1:x){
        print(strrep("*", i))
    }
}

yildiz.ciz(10)
## [1] "Yıldız çizdiriliyor"
## [1] "*"
## [1] "**"
## [1] "***"
## [1] "****"
## [1] "*****"
## [1] "******"
## [1] "*******"
## [1] "********"
## [1] "*********"
## [1] "**********"
carpim.tablosu = function(){
    for(i in 1:10){
        print(strrep("=", 30))
        for(j in 1:10){
            print(paste(i, "x", j, "=", i*j))
        }
    }
}

carpim.tablosu()
# sonucunu bilgisayarınızda gözlemleyebilirsiniz.
ortalama = function(yazili1, yazili2, sozlu, gecme.notu=50){
    sonuc = (yazili1 + yazili2 + sozlu) / 3
    if (sonuc > gecme.notu){
        print("geçti")
    }else{
        print("Kaldı")
    }
}

ortalama(45,75,70)
## [1] "geçti"
ortalama(45,75,70,70)  # gecme.notu 70 ise
## [1] "Kaldı"
tam.bolen.bul = function(x){
    bolenler = c()
    
    for (i in 1:x){
        if(x %% i == 0){
            bolenler = c(bolenler, i)
        }
    }
    bolenler
}

tam.bolen.bul(5)  # 5 in bölenleri
## [1] 1 5
tam.bolen.bul(15)  # 15 in bölenleri
## [1]  1  3  5 15
fibonacci = function(n){
    if(n == 1){
        return(1)
    }else if (n == 2){
        return(1)
    }else{
        return(fibonacci(n-1) + fibonacci(n-2) )
    }
}

for(i in 1:20)
    print(fibonacci(i))
## [1] 1
## [1] 1
## [1] 2
## [1] 3
## [1] 5
## [1] 8
## [1] 13
## [1] 21
## [1] 34
## [1] 55
## [1] 89
## [1] 144
## [1] 233
## [1] 377
## [1] 610
## [1] 987
## [1] 1597
## [1] 2584
## [1] 4181
## [1] 6765