Ruby/Söz Dizimi/Metod Çağrıları

←Kontrol Yapıları | Sınıflar→


Ruby'de method geriye değer dönen bir ifadeler gruudur. Metodlar sayesinde programcılar kodlarını daha sonra istedikleri yerden çağırabilecekleri kod parçaları şeklinde düzenlerlerDiğer diller buna bazen fonksiyon derler. Bir metod bir sınıfın parçası olduğu gibi bağımsız da olabilir (aslında bu durumda da "Main" sınıfının bir parçası olur).


Method Çağrıları

düzenle

Metodlar aşağıdaki ifadeyle çağrılır:

method_adı(parametre1, parametre2,)

Eğer metodun parametreleri yoksa parantez kullanılmadan sadece ad yazılarak da aşağıdaki gibi çağrılabilir:

method_adı

Ruby metodları parantezler kullanmadan da çağırmanıza izin verir:

results = method_adı parametre1, parametre2           # metodu parantez kullanmadan çağırmak
  
  # Eğer metodun sonucu hemen kullanılacaksa parantez kullanmak zorundasınız.
  # örneğin bir metod bir array dönecekse ve bunun elemanlarını ters sıralamak istersek:
  results = method_adı(parametre1, parametre2).reverse

Kolayca tahmin edilebilir ki, son satırda eğer parantez kullanmazsak , önce "parametre2.reverse işlemi yapılır, onun sonucu metoda ikinci parametre olarak verilir.


Metod Tanımlamaları

düzenle

Metodlar def deyimini takip eden metod ismi ile tanımlanır. Metod parametreleri , metod isminin arkasından parantez içinde belirtilir. Metod gövdesi bu tanımlama satırı ve aşağıda end deyiminin bulunduğu satır arasında kalan kısımdır. Ruby'cilerin genel teamülü bir'den fazla kelimeden oluşan metod isimlerinde kelimeleri alt çizgi ile ayırmaktır.

Örneğin:

def birşeyler_yaz(şey)
  puts şey 
end


Değer Dönüşleri

düzenle

Metodlar işlenen kodda oluşan en son değeri geri dönerler. Örneğin aşağıdaki metod x+y hesaplaması sonucunu deöner.

def hesap_yap(x, y)
  x + y
end

Dönen değeri daha açık ifade etmek isterseniz, metod bitmeden hemen öncesine return deyimi kullanırsınız. Bu deyim sayesinde end satırına gelmeden bir döngüden çıkarak ya da bir karşılaştırma işlemi sonucuna göre de metodu bitirip geri dönebilirsiniz.

def test(a, b)
  if a > b 
    return "ilk sayı büyük"
  else 
    return "Peynirli sandwiç"
  end
end

p test 12, 15

Not, bir blok içinde """return""" kullanırsanız bloğun dışına değil , metodun dışına çıkarsınız. Sadece bloğu sonlandırıp dışına çıkmak için break kullanılır. break deyimine bir argüman girerek bloktan dönen değer olarak kullanabilirsiniz :

six = (1..10).each {|i| break i if i > 5}

Bu kısa kod çalışınca "six" değişkeni "6" değerine sahip olacaktır.


Default Değerler

düzenle

Metod tanımlarken bir parametre için default bir değer de verilebilir, böylece metod eğer o parametreye bir değer vermeden çağrılırsa , default değer metod işletilirken kullanılır.

def bir_metod(değer='default', arr=[])
  puts değer
  puts arr.length
end

bir_metod('birşey')

Yukarıdaki kod şu çıktıyı verir:

  birşey
  0

Ruby 1.8 ve öncesi bu kod hata veriyordu:

def foo( i = 7, j ) # Syntax error in Ruby 1.8.7 Unexpected ')', expecting '='
    return i + j
end

Bu kod Ruby 1.9.2'de çalışır ve mantık olarak aşağıdaki ile arasında fark yıktur :

  def foo( j, i = 7)
    return i + j
  end


Değişken Sayıda Argümanlar , Yıldız Operatörü

düzenle

Bir metod tanımında son parametre başına bir yıldız (*-asterisk karakteri) konabilir. Buna bazen splat operatör de denir ve metoda daha fazla sayıda (değişken sayıda) parametre verilebileceğini ifade eder. Bu parametreler üretilen bir array içinde toplanır.

def değerleri_işle(x, y, *diğer_değerler)
    puts diğer_değerler
end
  
değerleri_işle(1, 2, 'a', 'b', 'c')

Bu örnekte çıkıt ['a', 'b', 'c'] şeklinde olacaktır.

Yıldız operatörü ayrıca metoda parametreleri bir array içinde vermek için metod çağırırken kullanılabilir.

arr = ['a','b','c']
değerleri_işle(*arr)

şu kodla aynı sonucu verir :

değerleri_işle('a', 'b', 'c')


Ruby'nin izin verdiği bir diğer teknik de metodlarla çalışırken size Hash argüman girme imkanı sunması, ki bu harika bir özellik. Değişken sayıda argümanı isimlendirilmiş olarak verebilirsiniz.

def hash_alabilir( var )
  p "aldıklarım : #{var.inspect}"  # aldığı argümanları yazar
end

hash_alabilir :arg1 => 'verilen arg1', :argN => 'verilen argN'
# =>"aldıklarım : {:arg1=>\"verilen arg1\", :argN=>\"verilen argN\"}"

Gördüğünüz üzere hash_alabilir metodu verilen Hash değeri işleyebiliyor. Bu teknik efsane web geliştirme ortamı Ruby On Rails tarafından çok etkili bir şekilde kullanılır.

Ayrıca hash argümanı girerken :arg1 => '...' kodu etrafında hash yapı tanımlarken kullandığımız { } süslü parantezlerini girmeye gerek olmadığını da görmüşsünüzdür. Yukarıdaki kod daha değişik ifadelerle şöyle de yazıılabilir :

hash_alabilir(:arg1 => 'verilen arg1', :argN => 'verilen argN')
# argüman listesi şeklinde parantez içinde virgülle ayrılmış
hash_alabilir( {:arg1 => 'verilen arg1', :argN => 'verilen argN'} )
# hash değer açık açık gösterilmiş


Ruby 2.0'dan beri isimlendirilmiş argümanlar girmek mümkündür, bu şekilde metod çağrı kodları daha açıklamalı olabilir. Deyim yapısı şöyle :

def test_method(a, b, c:true, d:false) 
   puts  a,b,c,d
end

Yukarıdaki metod şu şekillerde çağrılabilir :

test_method(1, 2)
test_method(1, 2, c: birdeğer)
test_method(1, 2, d: birdeğer)
test_method(1, 2, c: birdeğer, d: birdeğer)
# ve hatta
test_method(1, 2, d: birdeğer, c: birdeğer)

Tekrar söyleyelim, eğer metoddan dönen değeri zincirleme başka bir metoda bağlamayacaksanız parantezlere gerek yoktur. Bir şey daha belirtelim, bu şekil kullanımda ille de isim belirtmek zorundasınız. Yani test_method(1, 2, 3, 4) çalışmaz, default değerlerle karıştırılmasın.

Bu arada argüman kelimesini biraz açıklayalım. Metod tanımlanırken parametreler belirtilir. Metod çağrılırken bu parametrelere değerler verilerek çağrılır. İşte metodu çağırırken verdiğimiz bu değerlere "argüman" denir.

İsimlendirilmiş argümanlar özellikle birçok ihtiyaç duyulmama ihtimali olan parametresi olan metodların çağrılmasında çok faydalıdır. Ayrıca argüman değerlerini isimlendirerek kullanmak metod çağrısı kodunun çok daha okunabilir olmasını sağlar, Ruby'ciler tarafından çok kullanılır.


Ampersand Operatör - &

düzenle

Aynı asterisk gibi ampersand karakteri de metoda verilen son parametre önüne konan bir operatördür. Bu metodun parametresinde bir kod bloğu beklediğini ifade eder. Pas edilecek kodu içeren bir Proc nesnesi üretilir ve parametreye girilir.

Ayrıca ampersand operatöre benzer olarak, Proc nesnesi başına ampersand girilerek metoda parametre olarak verilirse metod içinde yield kullanılabilir.

def method_call
    yield
end

method_call(&birProc)


Blokları Anlamak, Proc ve Metodlar

düzenle

Ruby closure, yüksek seviye fonksiyon ve birinci sınıf gibi isimler verilen güçlü fonksiyonel programlama özelliklerini programcıya sağlar [1]. Bu özellikler Ruby'ye kod blokları, Proc nesneleri ve metodlarla kazandırılmış (bunlarda aslında birer nesnedir) — konseptler aşağı yukarı aynıdır ama küçük farklılıklar vardır.


Shivang's PROC

düzenle

Ruby dökümanlarına bakacak olursak Proc'lar şöyle tanımlanıyor : Proc nesneleri yerel değişkenlere bağlanabilen kod bloklarıdır. Bir kere bağlandı mı kod değişik şekillerde çağrılabilir.

Örnekler:

def gen_times(factor)
    return Proc.new {|n| n*factor }
end
  
times3 = gen_times(3)      # 'factor' 3 ile değiştirilir
times5 = gen_times(5)
  
times3.call(12)               #=> 36
times5.call(5)                #=> 25
times3.call(times5.call(4))   #=> 60


Procs'lar Ruby'de fonksiyon rolü oynarlar. Onları fonksiyon nesnesi diye çağırmak daha doğru olabilir, çünkü Ruby'de herşey bir nesnedir. Bu gibi nesnelere verilen bir isim vardır - functor. Functor sıradan bir fonksiyon gibi çağrılabilen bir nesne tanımlamasıdır, genellikle aynı deyim yapılarını kullanır, bu da tam olarak Proc nesnesidir.

Yukarıdaki tanımlama ve örneklere bakarsak, Ruby Proc'ların açıkça closure davranışı gösterdiği görülebilir.


Proc Hakkında Biraz Daha

düzenle

Proc'lar birinci sınıf nesnelerdir, çalışma zamanında üretilirler, veri yapılarında saklanırlar, diğer fonksiyonlara argüman olarak verilirler, bir fonksiyondan dönen değer olabilirler. Aslında yukarıdaki gen_times örneği tüm bunları gösteriyor, sadece diğer fonksiyona argüman olarak verilmesi yok. Bunu da şöyle gösterelim :

def foo (a, b)
    a.call(b)
end
  
putser = Proc.new {|x| puts x}
foo(putser, 34)


Ayrıca Proc nesnesi oluşturmanın bir kısa yolu var — Kernel metodu lambda [2] (metodlara geleceğiz ama şimdilik kernel metodu, her yerde çağırabileceğiniz global bir fonksiyona benzer olduğunu kabul ediniz). Lambda kullanarak önceki örnekteki Proc nesnesi şöyle üretilir :

  putser = lambda {|x| puts x}


Aslında lambda ve Proc.new arasında küçük farklar var. İlki argüman kontrolü. Lambda için Ruby dökümanı şunu der: Proc.new ile aynıdır, ama Proc nesneleri verilen argüman sayısını test eder. Buna bir örnek :

lamb = lambda {|x, y| puts x + y}
pnew = Proc.new {|x, y| puts x + y}

# düzgün çalışır, 6 yazar
pnew.call(2, 4, 11)

# ArgumentError hatası verir
lamb.call(2, 4, 11)


İkincisi return deyiminin işleyişinde görülür. Proc.new'den yapılan bir return işlemi, kapsayan metodu da bitirir :

def try_ret_procnew
    ret = Proc.new { return "Baaam" }
    ret.call
    "Buraya erişilemez"
end
  
# prints "Baaam"
puts try_ret_procnew

Ama lambda dönüşleri beklenen gibi hareket eder, çağıran yere değer döner :

def try_ret_lambda
    ret = lambda { return "Baaam" }
    ret.call
    "Bu yazılacak"
end
  
# prints "Bu yazılacak"
puts try_ret_lambda


Bunların ışığında sizlere, eğer diğerinin davranışı size gerekli değilse, Proc.new yerine lambda kullanmanızı öneririm. Hem iki harf daha kısa , hem daha düzgün görünüm hem de daha az süprizli.


Metodlar

düzenle

Basitçe söylemek gerekirse bir metod, bir kod bloğudur. Ancak Proc aksine metodlar yerel değişkenlere bağlanmaz. Yerine bir nesneye bağlanırlar ve onun oluşum değişkenlerine erişim yetkileri vardır [3]:

class Boogy
    def initialize
        @id = 15
    end
  
    def arbo
        puts "ID değeri #{@id}"
    end
end

# Boogy sınıfının bir oluşum nesnesini üretir
b = Boogy.new

b.arbo
# "ID değeri 15" yazar


Metodlar için kullanışlı görülen bir söylem, bağlı oldukları nesneye mesaj gönderdiğinizdir. Kendisine mesaj gönderdiğimiz bir alıcı vardır - metod tanımlanmış bir nesne -, ve adını verdiğimiz metodu belki de argümanları olacak şekilde içerir. Yukarıdaki örnekte arbo metodunu argüman olmadan çağırmak, sanki o nesneye sadece "arbo" diye bir mesaj göndermeye benzetilir.

Ruby'nin Object sınıfında (tüm nesnelerin ebeveyni olan sınıf) bulunan send metodu bu mesaj gönderme benzetmesini daha direk olarak gerçekleştirir. Aşağıda verilen 3 örnek arbo metodu çağırmanın alternatif yollarıdır :

# metod nesnede argümansız olarak direk çağrılır
b.arbo

# metod/mesaj gönderme isim string olarak verilmiş
b.send("arbo")
  
# metod/mesaj gönderme isim sembol olarak verilmiş
b.send(:arbo)


Not olarak, metodlar "top-level" kapsamda, yani betiğin root kapsamında, bir sınıfa ait olmadan da tanımlanabilir. Örneğin :

def say (something)
    puts something
end
  
say "Hello"


Her ne kadar say metodu kendi kendine duruyormuş gibi görünse de Ruby onu sessizce uygulamanızın kapsamı olan Object sınıfına bağlar:

def say (something)
    puts something
end
  
say "Hello"
Object.send(:say, "Hello") # yukarıdaki satırla aynı işi yapar

Bu arada daha önceye nesneye mesaj göndermek için kullandığımız send metodunu şimdi Object sınıfına mesaj göndermek için kullandığımız dikkatinizi çekmiş olabilir. Öyle tanımlanmış olmalı, aynısını az önce tanımladığımız "Boogy" metodu için yapamayız.

b.arbo          #=> ID değeri 15
Boogy.arbo      
#=> undefined method `arbo' for Boogy:Class (NoMethodError)


say metoduna geri dönelim, her şeye rağman biz hala bu metodu tüm pratik kullanım amaçlarımızda bağımsız bir metod olarak düşünebiliriz. Perl ve C gibi bazı dillerde bunlara fonksiyon denir. Aşağıdaki Proc da birçok yönden benzerdir :

say = lambda {|birşey| puts birşey}
  
say.call("Hello")

# aynı etki
say["Hello"]

[ ] yapısı Proc call ile aynı işi yapar [4]. Bununla beraber metodlar, Proc'lara nazaran Ruby'nin önemli bazı özelliklerini destekler. Bunu blokların ne olduğunu gördükten sonra anlatacağız.


Bloklar

düzenle

Bloklar Procs ile o kadar güçlü bir şekilde ilişkilidir ki, birçok yeni başlayanın aslında nasıl farklı olduklarını anlamaya çalışması baş ağrısına neden olur. Anlaşılmasını kolaylaştırmak için (umarım çok da klişe olmayan) bir metafor kullanmaya çalışacağım. Benim görüşüme göre bloklar doğmamış Proc'lardır. Bloklar larva ise Proc'lar böceklerdir. Bir blok kendi kendine yaşayamaz - kodu yaşama hazır hale getirir ve bir Proc'a bağlanıp dönüştürüldüğünde yaşamaya başlarlar :

  # çıplak bir blok Ruby içinde yaşayamaz
  # şu kod derleme hatası verir !
  {puts "hello"}
  
  # bir Proc'a dönüştürülünce yaşamaya başlar !
  pr = lambda {puts "hello"}
 
  pr.call

İşte bu kadar, bütün bu yaygara bunun için mi, o zaman? Hayır, hepsi bu değil. Ruby'nin tasarımcısı Matz şunu der, Proc'ları metodlara göndermek (ve diğer Proc'lara) güzeldir ve üst düzey işlevlere ve her türlü büyülü işlevsel şeye izin verir, diğer tüm herşeyin üstünde duran bir şey vardır - bir blok kodu bir metoda göndermek çok yararlı işler yapmanızı sağlayabilir, mesela iterasyon. Ve çok yetenekli bir tasarımcı olarak Matz, bu özel durumu vurgulamanın ve onu hem daha basit hem de daha verimli hale getirmenin değerli olduğuna karar verdi.


Bir bloğu bir metoda göndermek

düzenle

Hiç şüphe yok ki Ruby ile en azından birkaç saat geçiren herhangi bir programcıya Ruby'nin ihtişamının aşağıdaki örnekleri (veya buna çok benzer şeyler) gösterilmiştir:

10.times do |i|
    print "#{i} "
end
  
numbers = [1, 2, 5, 6, 9, 21]
  
numbers.each do |x|
    puts "#{x} değeri " + (x >= 3 ? "çok" : "az")
end
  
squares = numbers.map {|x| x * x}
p squares

(Not olarak do |x| ... end ile { |x| ... } eşleniktir )

Benim naciz fikrime göre bu şekil kodlama Ruby'yi kolay okunabilir ve harika bir dil yapıyor. Burada arka planda olup bitenler oldukça basittir veya en azından çok basit bir şekilde anlatılabilir. Gerçak hayata yakın ve kolay anlaşılabilir bir kod olması için herşeyi yapmışlar.

Bir metod çağrısı arkasına bir blok eklendiğinde, Ruby bloğu bir Proc nesnesine dönüştürür, sadece özel bir adı olmaz. Ancak metodun bu bloğa erişmek için bir yolu vardır, yield deyimi kullanmak. Açıklamak için şu örneğe bakalım :

def do_twice
    yield 
    yield
end

do_twice {puts "Hola"}


do_twice metodu tanımlanmış ve bir blok eklenerek çağrılmış. Metod , parametre listesinde bir blok alacağını belirtmediği halde yield deyimi bloğu çalıştırır. Bu metod tanımında Proc bir parametre kullanarak daha açık ifade edilebilir:

def do_twice(what)
    what.call
    what.call
end

do_twice lambda {puts "Hola"}

Bu önceki örnekle eşdeğer, fakat blokları yield ile kullanmak eğer sadece bir blok metoda gönderilecekse daha temiz olacaktır. Proc şeklini kullanarak çok sayıda blok metoda gönderilebilir :

def do_twice(what1, what2, what3)
    2.times do
        what1.call
        what2.call
        what3.call
    end
end

do_twice(   lambda {print "Hola, "},
            lambda {print "querido "},
            lambda {print "amigo\n"})


Birçok kişinin blok göndermekten hoşlanmadığını ve bunun yerine açık Proc'ları tercih ettiğini belirtmeliyiz. Onların fikrine göre Proc parametreler metod tanımında en başta açıkça görülürken, bloklar örtülüdür ve tüm metod tanımı içinde yield kullanılmış mı diye bakmak gerekir. Bu tamamen bir zevk meselesi olsa da her iki yaklaşımı da anlamak hayati önem taşır.


Ampersand (&)

düzenle

Ampersand operatörü birkaç durumda bloklar ve Proc'lar arasında dönüştürme işlemlerinde kullanılabilir. Bunların nasıl çalıştığını anlamaya değer.

Metod çağırırken isminin arkasına eklenen bir bloğun, arka planda bir Proc nesnesine dönüştürüldüğünü söylemiştik. Pekala , eğer metod tanımında son parametre adı önüne bir ampersand işareti konursa metod, argümanında verilen bloğu bir Proc nesnesine dönüştürür ve o son parametre adıyla içindeki kodda kullanabilir :

def uydurma(a, &f)
    # f üzerinden bloğa ulaşılabilir
    f.call(a)
      
    # ama yield de çalışır !
    yield(a)
end
  
# bu çalışır
uydurma(25) {|x| puts x}
  
# bu ise ArgumentErrorverir çünkü &f 
# gerçek bir argüman değildir - sadece
# bir blok dönüştürüleceği için oradadır
uydurma(25, lambda {|x| puts x})


İkinci metod çağrısının çalışması için metod tanımında ampersand kullanmadan parametre ismi vermek gerekir ki, bu durumda hem yield hem de ilk metod çağrısı hata oluşturacaktır.

Bir diğer (bana göre daha etkili) ampersand kullanımı ise diğer yönde dönüştürmesidir — bir Proc nesneyi bir bloğa dönüştürme. Bu Ruby'nin büyük dahili kabiliyetlerinden, özellikle iterasyonlar için, argümanda bir blok almasına yarar ve bazen bir Proc nesnesi göndermekten daha uygun olur. Aşağıdaki örnek pragmatic programmers'ın efsane “Programming Ruby” kitabından alınmıştır :

print "(t)imes or (p)lus: "
times = gets
print "number: "
number = Integer(gets)
if times =~ /^t/
    calc = lambda {|n| n*number }
else
    calc = lambda {|n| n+number }
end
puts((1..10).collect(&calc).join(", "))


collect metodu bir blok bekler, ama bu durumda bir Proc vermek kullanıcıya daha uygun gelmiştir. calc öncesine eklenen ampersand ile Proc nesnesi olan calc'ın bir bloğa dönüştürülmesi ve collect metoduna eklenen bir blok olarak verilmesi sağlanmıştır.

Ampersand işareti aynı zamanda Ruby programcıları arasında çok yaygın olan bir deyimin uygulanmasına da olanak tanır: metod isimlerini iteratörlere göndermek. Diyelim bir array içindki tüm kelimeleri büyük harfe dönüştürmek istiyorum. Bunu şöyle yapabiliriz :

words = %w(Jane, aara, multiko)
upcase_words = words.map {|x| x.upcase}
  
p upcase_words   #=> ["JANE,", "AARA,", "MULTIKO"]


Bu çok güzel ama ben bunu biraz fazla ayrıntılı buluyorum. upcase metodu direk olarak map metoduna verilmeli ve ayrı bir blok ile gereksiz x argümanı kullanılmamalıydı. Ne mutluki , daha önce deiğimiz gibi, Ruby nesnelere mesaj göndermeyi destekler, ve metodlar isimleri Ruby sembolü gibi yazılarak ifade edilebilir. Örneğin :

  p "Erik".send(:upcase)


Bu ifade "upcase mesajını 'Eric' nesnesine gönder" demek. Bu özellik kullanılarak map {|x| x.upcase} yakışıklı bir hale gelebilir, ve bunun için ampersand kullanacağız ! Demiştik ki, bir metod çağrısında ampersand kullanıldı mı, verilen Proc nesnesini bir bloğa dönüştürür. Fakat ya bir Proc nesnesi değil de başka bir nesneye uygulanırsa ? İşte o zaman Ruby'nin gizli tip dönüşümleri devreye girer ve nesnede to_proc metodu çağrılarak nesne Proc nesnesine dönüştürülmeye çalışılır. Symbol sınıfı için de to_proc metodu şuna benzer birşeydir :

class Symbol
      
    # A generalized conversion of a method name
    # to a proc that runs this method.
    #
    def to_proc
        lambda {|x, *args| x.send(self, *args)}
    end
      
end

Bunu hesaba katarak yukarıdaki kodumuzu şöyle yazabiliriz :

words = %w(Jane, aara, multiko)
upcase_words = words.map(&:upcase)
  
p upcase_words

Yaa, yaa, yaa gördünüz mü?


Dinamik Metodlar

düzenle

Ruby'de sadece bir nesne için metod tanımlanabilir.

a = 'b'
def a.some_method
    'sadece a için yazılmış bir metod'
end
p a.some_method
#=> 'sadece a için yazılmış bir metod'

Ya da tanımlamanın bulunduğu kapsamı koruyan define_singleton_method kullanabilirsiniz.

x = 5
a = 'b'
a.define_singleton_method(:some_method) {
    p "x = #{x}"
}
a.some_method

def...end bloğu içinden dışarıdaki x değerine erişilemez.


Özel Metodlar

düzenle

Ruby interpreter tarafından çağrılan bir kısım metodlara sahiptir. Örneğin :

class Chameleon     #Bukalemun
    alias __inspect__ inspect
    def method_missing(method, *arg)
        if (method.to_s)[0..2] == "to_"
            @identity = __inspect__.sub("Chameleon", method.to_s.sub('to_','').capitalize)
            def inspect
                @identity
            end
            self
        else
            super #method_missing default Kernel.method_missing üzerine yazar
              #ilgilenmediğimiz bir metod çağrılmışsa orjinali dönelim ki
              #bizim Bukalemun farkedilmesin ;)
        end
    end
end
mrlizard = Chameleon.new
p mrlizard
p mrlizard.to_rock
p mrlizard
p mrlizard.bişey #undefined method `bişey' for #<Rock:0x0...> (NoMethodError)

Bu kod saçma birşeyler yapıyor. Ama method_missing Ruby'de meta-programming'in önemli bir parçası. Ruby on Rails'de bu dinamik metodlar üretmek için çok kapsamlı kullanılır.

Bir diğer özel metod da Ruyb'nin bir sınıfın oluşum nesnesini her ürettiğinde çağırdığı initialize metodudur, ama bu sonraki bölümün konusu: Sınıflar.


Ruby'de diğer diller benzeri fonksiyon yapısı yoktur. Yerine biraz farklı iki konsept vardır — metodlar ve Proc'lar (gördüğümüz kadarıyla diğer dillerde fonksiyon nesnesi ya da fonksiyon denen yapılar). Her ikisi de kod bloklarıdır — metodlar nesnelere bağlanır, ve Proc'lar da bulunulan kapsamdaki yerel değişkenlere. Kullanımları biraz farklıdır.

Metodlar nesne yönelimli programlamanın mihenk taşlarıdır, ve Ruby tamamen O-O (Object Oriented) bir dil olduğu için, metodlar Ruby'nin en doğal parçasıdır. Metodlar, Ruby nesnelerinin yaptığı hareketlerdir — nesnelere gönderilen mesajlardır, eğer siz mesaj gönderme düşüncesini tercih ediyorsanız.

Proc'lar, kod bloğunu birinci sınıf Ruby nesnesine dönüştürerek güçlü fonksiyonel programlama paradigmalarını mümkün kılar. Bunlar Lisp'in lambda formlarına benzer (bu Ruby'nin Proc oluşturucusu lambda'nın kökeni hakkında çok az şüphe veriyor)

Blok yapısı ilk bakışta kafa karıştırıcı gelebilir, ancak aslında oldukça basittir. Bir blok yapısı , bana göre , doğmamış bir Proc'dur — daha herhangi bir şeye bağlanmamış bir Proc'dur. Sanırım bu Ruby'de blokları kafa karıştırmadan anlamanın en kolay yolu, bloklar bir değişik formatta Proc'tur. Burada blokların Proc'lardan farkını gördüğümüz yer, metodlara son parametre olarak gönderildiğinde , metod içinde yield ile çalıştırılması.

Sanırım bu kadar. Bu sayfa için yürüttüğüm araştırmanın birçok yanlış anlaşılmayı giderdiğini kesin olarak biliyorum. Katılmadığınız bir şey görürseniz - bariz hatalardan küçük yanlışlıklara kadar - kitabı düzeltmekten çekinmeyin.

[1] It seems that in the pure, theoretical interpretation what Ruby has isn’t first-class functions per se. However, as this article demonstrates, Ruby is perfectly capable of fulfilling most of the requirements for first-class functions, namely that functions can be created during the execution of a program, stored in data structures, passed as arguments to other functions, and returned as the values of other functions.

[2] lambda has a synonym - proc, which is considered ‘mildly deprecated’ (mainly because proc and Proc.new are slightly different, which is confusing). In other words, just use lambda.

[3] These are ‘instance methods’. Ruby also supports ‘class methods’, and ‘class variables’, but that is not what this article is about.

[4] Or more accurately, call and [] both refer to the same method of class Proc. Yes, Proc objects themselves have methods !


Önceki: Söz Dizimi/Kontrol Yapıları Index Sonraki: Söz Dizimi/Sııflar