Proc ve Lambda
Block kullanımını daha dinamik ve programatik hale getirmek için Procedures ya da genel adıyla Procs, object-oriented Ruby'nin vazgeçilmezlerindendir.
Bazen, her seferinde aynı bloğu sürekli geçmek gerektiğinde imdadımıza Proc yetişiyor. Nasıl mı?
Düşünün ki bir method'unuz (fonksiyonunuz) olsun, ve bu method’u dinamik olarak programlayabilseniz?
def multiplier(with)
return Proc.new {|number| number * with }
end
Çarpma yaptıran dinamik bir fonksiyon. Sayıyı kaç ile çarpacaksak with
e o
sayıyı geçiyoruz.
multiply_with_5 = multiplier(5)
Elimizde geçtiğimiz sayıyı 5 ile çarpacak, fonksiyondan türemiş bir
fonksiyon oluştu. Eğer multiply_with_5
acaba neymiş dersek?
puts multiply_with_5
# #<Proc:0x007fcc9c94a938@/Users/vigo/Desktop/ruby101_book_tests.rb:2>
puts multiply_with_5.class
# Proc
Gördüğünüz gibi elimizde bir adet Proc
objesi var!. Haydi kullanalım!
puts multiply_with_5.call(5) # 25
puts multiply_with_5.call(10) # 50
Bu kadar kasmadan, basit bir method ile yapsaydık:
def multiplier(number, with)
return number * with
end
puts multiplier 5, 5 # 25
şeklinde olurdu. Benzer bir örnek daha yapalım:
multiplier = Proc.new { |*number|
number.collect { |n| n ** 2 }
}
multiplier.call(1) # => [1]
multiplier.call(2,4,6) # => [4, 16, 36]
multiplier[2,4,6].class # => Array
Az ileride Array
konusunda göreceğimiz collect
method'unu kullandık. Bu
method ile Array'in her elemanını okuyor ve karesini n ** 2
alıyoruz. *
işareti yine Array'de göreceğimiz splat özelliği. Yani geçilen parametre
grubunu Array olarak değerlendir diyoruz.
Lambda
Python'la uğraşan okurlarımız Lambda'ya aşina olabilirler. Proc ile ilk göze çarpan fark, argüman olarak geçilen parametrelerin sayısı önemlidir Lambda'da. Aynı Proc gibi çalışır.
custom_print = lambda { |txt| puts txt }
custom_print.call("Hello") # Hello
Eğer 2 parametre geçseydik:
custom_print = lambda { |txt| puts txt }
custom_print.call("Hello", "World")
# ArgumentError: wrong number of arguments (2 for 1)
Ya da;
l = lambda { "Merhaba" }
puts l.call # Merhaba
Başka bir kullanım;
l = lambda do |user_name|
if user_name == "vigo"
"Selam vigo! nasılsın?"
else
"Selam sana #{user_name}"
end
end
puts l.call("vigo") # Selam vigo! nasılsın?
puts l.call("uğur") # Selam sana uğur
Proc ve Lambda Farkı
def arguments(input)
one, two = 1, 2
input.call(one, two)
end
puts arguments(Proc.new{ |a, b, c| puts "#{a} ve #{b} ve #{c.class}" })
# 1 ve 2 ve NilClass
puts arguments(lambda{ |a, b, c| puts "#{a} ve #{b} ve #{c.class}" })
# ArgumentError: wrong number of arguments (2 for 3)
Aynı kodu kullandık, Lambda yeterli parametre almadığı için hata verdi.
Proc'u Block'a çevirmek
Elimizdeki Array elemanlarının her birini bir fonksiyona tabii tutsak? Dizinin her elemanını ekrana yazdıran bir şey?
items = ["Bu bir", "bu iki", "bu üç"]
print_each_element = lambda { |item| puts item }
items.each(&print_each_element)
Bu örnekte items.each(&print_each_element)
satırı Proc'u Block'a
çevirdiğimiz yer. &
işin sırrı. each
'den gelen eleman, sanki method
çağırır gibi print_each_element
e pas ediliyor, karşılayan da {
}
içinde
tanımlı kod bloğunu çalıştırıyor.
Keza aynı işi;
items = ["Bu bir", "bu iki", "bu üç"]
def print_each_element(item)
puts item
end
items.each(&method(:print_each_element))
şeklinde de yapabilirdik!