Ruby'de bir metodu def anahtar kelimesi, bir metot adı, isteğe bağlı parametre listesi, gövde ve end ile tanımlarsınız. Metot, gövdede en son değerlendirilen ifadenin sonucunu kendiliğinden döndürür; bu yüzden çoğu zaman return yazmanız gerekmez. Parametrelere varsayılan değer verebilir, *args ile sınırsız sayıda argüman toplayabilir ve tek ifadeden oluşan metotları def kare(x) = x * x biçiminde tek satırda yazabilirsiniz.

Bu yazıda metot tanımlamanın temel kurallarını, dönüş değerinin nasıl belirlendiğini ve konumsal (positional) parametre türlerini örneklerle anlatıyoruz. Tüm örnekler Ruby 4.0 ile çalıştırıldı; yorumlardaki # çıktı: satırları gerçek çıktıdır. Keyword argümanları ayrı bir konu olduğu için anahtar kelime argümanları yazısında, bloklar ise blok ve yield yazısında işleniyor. Ruby'yi genel olarak tanımak isterseniz Ruby nedir? yazısıyla başlayabilirsiniz.

def ile ilk metot

def ile ilk metot

En basit metot, parametre almayan ve tek bir ifade döndüren metottur. Parametreler metot adından sonra parantez içinde virgülle ayrılarak yazılır ve gövdede yerel değişken gibi kullanılır. Çağırırken argümanları tanımdaki sırayla vermeniz gerekir; Ruby bunlara konumsal argüman der. Tanımda parantezler isteğe bağlıdır (def ekle deger da geçerlidir), ancak okunabilirlik için parametre varken parantez kullanmak yaygın bir alışkanlıktır.

ruby
def selamla(ad)
  "Merhaba, #{ad}!"
end

def bolum(a, b)
  return "sıfıra bölünemez" if b.zero?
  a / b
end

p selamla("Ayşe") # çıktı: "Merhaba, Ayşe!"
p bolum(10, 2)    # çıktı: 5
p bolum(10, 0)    # çıktı: "sıfıra bölünemez"

Dönüş değeri: örtük return ve erken çıkış

Ruby belgelerine göre bir metot, varsayılan olarak gövdesinde değerlendirilen son ifadeyi döndürür. Yukarıdaki selamla metodunda son ifade metin şablonudur, dolayısıyla dönüş değeri de odur. return anahtar kelimesi iki durumda işe yarar: dönüşü açıkça göstermek istediğinizde ve metottan son satıra gelmeden çıkmak istediğinizde. bolum metodundaki return ... if b.zero? satırı, bölen sıfırsa metodu hemen bitirir; altındaki a / b hiç çalışmaz.

Atama metotlarında (adı = ile bitenler) bir istisna vardır: nesne.deger = 5 biçiminde çağırdığınızda metodun döndürdüğü değer yok sayılır ve ifadenin sonucu her zaman verdiğiniz argüman olur. Metodu send ile doğrudan çağırırsanız gerçek dönüş değerini görürsünüz:

ruby
class Sayac
  def deger=(v)
    @deger = v
    return 99
  end
end

s = Sayac.new
p(s.deger = 5)       # çıktı: 5
p s.send(:deger=, 5) # çıktı: 99

Metot adları: ?, ! ve = ile biten adlar

Metot adları küçük harfle ya da alt çizgiyle başlar, kelimeler alt çizgiyle ayrılır (toplam_tutar). Belgeler üç özel son eki tanımlar. ? ile biten metotlar gelenek olarak doğru/yanlış sorusuna cevap verir; ama her zaman yalnızca true ya da false döndürmek zorunda değildir, doğru kabul edilen başka bir nesne de dönebilir. ! ile biten metotlar "tehlikeli" sürüm kabul edilir; çekirdek kütüphanede bu genellikle alıcı nesneyi yerinde değiştirdiği anlamına gelir ve çoğunun ünlemsiz, nesneyi değiştirmeyen bir eşi vardır. = ile bitenler ise yukarıda gördüğünüz atama metotlarıdır.

ruby
kelime = "merhaba"
p kelime.upcase # çıktı: "MERHABA"
p kelime        # çıktı: "merhaba"
kelime.upcase!
p kelime        # çıktı: "MERHABA"

Ayrıca +, ==, <=>, [] gibi operatörleri de metot olarak tanımlayabilirsiniz. Belgeler, operatörlere beklenmedik anlamlar yüklemenin kafa karıştıracağı uyarısını yapar ve operatör önceliğinin değiştirilemeyeceğini belirtir. Operatörlerin sırası için Ruby operatörleri ve öncelik yazısına bakabilirsiniz.

Varsayılan değerli parametreler

Varsayılan değerli parametreler

Bir parametreye = ile varsayılan değer verirseniz çağıran o argümanı atlayabilir. Varsayılan değer, kendisinden önce gelen parametrelere başvurabilir, çünkü parametreler soldan sağa değerlendirilir. Varsayılanlı parametrelerin listenin sonunda olması gerekmez, ancak hepsinin yan yana durması gerekir: def f(a = 1, b, c = 1) gibi bölünmüş bir tanım SyntaxError verir.

ruby
def fiyat(tutar, kdv = 20)
  tutar + tutar * kdv / 100
end
p fiyat(100)     # çıktı: 120
p fiyat(100, 10) # çıktı: 110

def ekle(a = 1, b = a)
  a + b
end
p ekle    # çıktı: 2
p ekle(5) # çıktı: 10

def dizi(a, b = 2, c = 3, d)
  [a, b, c, d]
end
p dizi(1, 4)    # çıktı: [1, 2, 3, 4]
p dizi(1, 5, 6) # çıktı: [1, 5, 3, 6]

Son örnek ilk bakışta şaşırtabilir. Ruby önce zorunlu parametreleri doldurur: 1 değeri a'ya, son argüman d'ye gider. Geriye kalan argümanlar varsayılanlı parametrelere soldan sağa dağıtılır; bu yüzden üç argümanlı çağrıda 5 değeri b'ye gider ve c varsayılan 3 değerinde kalır.

Splat: *args ile değişken sayıda argüman

Bir parametrenin önüne * koyarsanız, geri kalan konumsal argümanların hepsi o parametrede bir Array olarak toplanır. Bu parametre listenin başında, ortasında ya da sonunda olabilir; ama keyword argümanlarından önce gelmelidir. Çağrı tarafında da * kullanarak bir diziyi ayrı argümanlara açabilirsiniz. Ekstra parantezle yazılan bir parametre ise gelen diziyi parçalarına ayırır (array decomposition).

ruby
def topla(*sayilar)
  sayilar.sum
end
p topla          # çıktı: 0
p topla(1, 2, 3) # çıktı: 6

def orta(ilk, *ara, son)
  ara
end
p orta(1, 2, 3, 4) # çıktı: [2, 3]

liste = [4, 5, 6]
p topla(*liste) # çıktı: 15
p topla(*nil)   # çıktı: 0

def ayir((ad, *diger))
  [ad, diger]
end
p ayir(["Ali", "Can", "Ece"]) # çıktı: ["Ali", ["Can", "Ece"]]

Yalnızca * yazıp ad vermezseniz argümanlar yok sayılır ya da (Ruby 3.2 ve sonrasında) başka bir metoda aynen aktarılabilir. Bu anonim yönlendirme ayrıntısı keyword argümanları yazısında anlatılıyor.

Endless metot tanımı: tek satırlık def

Belgelere göre Ruby 3.0 ile, tek bir ifadeden oluşan metotlar için kısa bir yazım geldi: def ad(parametre) = ifade. Ruby 3.0 NEWS dosyası bu özelliği eklendiği sürümde deneysel olarak işaretlemişti. Ruby 3.1 NEWS dosyası ise bu yazımda komut biçiminin de kullanılabildiğini, yani def foo = puts "Hello" yazılabildiğini, buna karşın private def foo = puts "Hello" satırının ayrıştırılamadığını not eder.

ruby
def kare(x) = x * x
def bos? = true

p kare(7) # çıktı: 49
p bos?    # çıktı: true

Bu yazımın iki kuralı var. Birincisi, parametre varsa parantez zorunludur: def ekle(deger) = deger + 1 geçerlidir, def ekle deger = deger + 1 ise SyntaxError verir. İkincisi, atama metotları (deger=) bu kısa yazımla tanımlanamaz.

Tekil metotlar ve metot içinde rescue

Metodu belirli bir nesneye de ekleyebilirsiniz. def nesne.ad yazımı yalnızca o nesneye ait bir "singleton method" üretir; aynı sınıftaki diğer nesnelerde bu metot olmaz. Sınıf metodu dediğimiz def self.ad da aslında bunun özel bir durumudur. Ayrıca her metot gövdesi örtük bir hata yakalama bloğu gibi davranır: begin yazmadan doğrudan rescue, else ve ensure ekleyebilirsiniz.

ruby
metin = "Ruby"
def metin.cerceve
  "[#{self}]"
end
p metin.cerceve                    # çıktı: "[Ruby]"
p "Python".respond_to?(:cerceve)   # çıktı: false

def guvenli_bol(a, b)
  a / b
rescue ZeroDivisionError
  :hata
ensure
  puts "bitti"
end
p guvenli_bol(6, 3) # çıktı: bitti
                    # çıktı: 2
p guvenli_bol(6, 0) # çıktı: bitti
                    # çıktı: :hata

ensure bölümü her durumda çalışır ama metodun dönüş değerini değiştirmez; dönüş değeri gövdeden ya da rescue bölümünden gelir. Hata yönetiminin ayrıntıları için begin, rescue ve ensure yazısına bakın. Sınıf içindeki metotlar ve görünürlük için de class ve nesneler ile public, private ve protected yazıları var.

Parametre türleri bir bakışta

Parametre türleri bir bakışta: def f(a), def f(a = 1), def f(*a), def f((a, b))
YazımAnlamıÇağrı örneği
def f(a)Zorunlu konumsal parametref(1)
def f(a = 1)Varsayılan değerli parametref veya f(5)
def f(*a)Kalan konumsal argümanları dizi olarak toplarf(1, 2, 3)
def f((a, b))Gelen diziyi parçalarına ayırırf([1, 2])
def f(*)Adsız splat, yok sayar veya aktarırf(1, 2)
def f(a:)Zorunlu keyword (ayrı yazıda)f(a: 1)
def f(&b)Bloğu Proc olarak alır (ayrı yazıda)f { }
def f(x) = x * 2Endless (tek satırlık) tanımf(3)

Sık yapılan hatalar

  • Eksik argümanla çağırmak. Zorunlu parametre sayısı tutmazsa ArgumentError alırsınız; mesajda kaç argüman verildiği ve kaç tane beklendiği yazar.
  • Varsayılan değerde sonraki parametreye başvurmak. Parametreler soldan sağa değerlendirildiği için def ters(a = b, b = 1) çağrıldığında b henüz tanımlı değildir ve NameError oluşur.
  • Varsayılanlı parametreleri bölmek. def f(a = 1, b, c = 1) sözdizimi hatasıdır; varsayılanlılar bir arada durmalıdır.
  • Endless tanımda parantezi unutmak. def ekle deger = deger + 1 geçersizdir.
  • Atama metodunun dönüş değerine güvenmek. nesne.x = 5 ifadesi her zaman 5 değerini verir.
ruby
def iki(a, b) = a + b
begin
  iki(1)
rescue ArgumentError => e
  puts "#{e.class}: #{e.message}"
end
# çıktı: ArgumentError: wrong number of arguments (given 1, expected 2)

def ters(a = b, b = 1)
  a + b
end
begin
  ters
rescue NameError => e
  puts "#{e.class}: #{e.message}"
end
# çıktı: NameError: undefined local variable or method 'b' for main

Ruby 4.0 NEWS dosyasına göre ArgumentError oluştuğunda ErrorHighlight artık hem çağrının yapıldığı satırı hem de metodun tanımlandığı satırı kod parçası olarak gösteriyor. Bu sayede yanlış sayıda argümanla yapılan çağrıları terminalde daha kolay bulursunuz.

Sık Sorulan Sorular

Ruby'de return yazmak zorunlu mu?

Hayır. Metot son değerlendirilen ifadeyi döndürür. return yalnızca metottan erken çıkmak ya da dönüşü açıkça göstermek istediğinizde gereklidir.

Metot çağırırken parantez kullanmalı mıyım?

Çoğu durumda parantez isteğe bağlıdır. Ancak iç içe çağrılarda belirsizlik oluşabilir; belgeler method_one argument1, method_two argument2, argument3 gibi bir satırın SyntaxError verdiğini gösterir. Argüman varken parantez kullanmak bu tür sorunları önler.

Endless metot ne zaman kullanılmalı?

Gövdesi tek bir ifade olan kısa metotlarda, örneğin hesaplama ya da basit sorgu metotlarında. Birden fazla adım, rescue ya da atama metodu gerekiyorsa klasik def ... end yazımı gerekir veya daha okunur olur.

Bir metot birden fazla değer döndürebilir mi?

Doğrudan değil, ama bir dizi döndürüp çoklu atamayla açabilirsiniz: ad, diger = ayir([...]). Çoklu atamanın ayrıntıları için değişkenler ve sabitler yazısına bakabilirsiniz.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Ruby öğrenme rehberi · Keyword argümanları · Blok ve yield · Proc ve lambda farkı · Hata yönetimi · Ruby nedir?