Ruby’ de Block & Proc & Lambda & Closure Nedir?

Ata Günay
Fazla
Published in
4 min readNov 15, 2022

Method İçinde Method

Bir methoda parametre olarak başka bir method göndermek sizce mümkün müdür? Sorumu koda dökücek olursak:

method_1()

method_2(method_1)

C Dilinde Pozitif Sayıları Ekrana Bastırma

Şimdi bunu biraz pratik edelim. Bir dizimiz olduğunu varsayalım. Dizilerdeki elemanları tek tek gezerek pozitif olanları ekrana bastırmak istiyoruz. Ruby’ deki block, proc ve lambda yapılarını daha iyi anlayabilmek adına low level bir dil olan C ile kıyaslayarak gideceğim.

C dilindeki kullanım:

Burada gördüğünüz gibi iki adet önemli keyword vardır: for ve if.

if ile oluşturduğumuz kontrol mekanizmamızı bir fonksiyonun içine taşıyabiliriz aslında.

Eğer for döngümüz de bir metod olsaydı karşımıza şöyle bir kullanım çıkabilirdi:

travel_array(arr, 5, print_if_positive)

Dizimizi, eleman sayısını ve çağıracağı fonksiyonu parametre olarak veririrdik. Yani en başta değindiğimiz bir fonksiyona başka bir fonksiyonu parametre olarak geçmiş olurduk.

Ruby’ de Block

Method içinde method’ un mantığını anladığımıza göre gelelim Ruby’ deki bloklara

  • do — end veya { } karakterleri arasına yazılır.
  • metodlara parametre olarak bir metod göndermek istediğimiz zaman kullanılır.
  • parametre olarak göndereceğimiz metod parametre alıyorsa parametreler | | karakterleri arasında tanımlanır.

İşte yukarıdaki örneğin Ruby’ de bloklar ile kullanılması:

Eğer Ruby’ nin hazır methodları dışında kendi tanımladığınız methodlara da bir block geçmek istiyorsanız karşınızda “yield” keywordu.

def print_once
yield
end
print_once { puts "Block is being run" }
def one_two_three
yield 1
yield 2
yield 3
end
one_two_three { |number| puts number * 10 }
# 10, 20, 30

Buradaki önemli kısım Ruby’ nin methodunuzun içinde kullandığınız yield keywordu ile bloğunuzu eşleyebilmesi için method ve block isimlerinin aynı olması gerekmektedir.

Ruby’ de Lambda

Lambda ile bir block tanımlayarak bunu bir değişkene atayabiliriz. Daha sonrasında ise “call” keywordu ile çalıştırırız.

say_something = -> { puts "This is a lambda" }
say_something.call
# "This is a lambda"

“->” yerine “lambda” kullanabilirsiniz

say_something = lambda { puts "This is a lambda" }
say_something.call
# "This is a lambda"

Lambdalar bir çok şekilde çağrılabilir fakat en çok tercih edileni “call” ile çağırmaktır.

my_lambda = -> { puts "Lambda called" }
my_lambda.call
my_lambda.()
my_lambda[]
my_lambda.===

Lambdalarda parametrelere de izin verilmektedir.

times_two = ->(x) { x * 2 }
times_two.call(10)
# 20

Ruby’ de Proc

Lamda ile aynı şekilde çalışır. Bir block tanımlayarak bunu bir değişkene atarız ve “call” ile çalıştırırız. Lamda ile arasında küçük farklar vardır:

  1. Tanımlama şekli
t = Proc.new { |x,y| puts "I don't care about arguments!" }
t.call
# "I don't care about arguments!"

2. Neyi return ettiği

Lambda verdiğiniz değeri geri döndürürken Proc bir zıplama işlemi yaparak bulunduğunuz konumdan geriye zıplamanızı sağlar.

def call_proc
puts "Before proc"
my_proc = Proc.new { return 2 }
my_proc.call
puts "After proc"
end

p call_proc
# Prints "Before proc" but not "After proc"

Proc burada bir zıplama işlemi yaparak metodun akışını sonlandırmıştır. Bu sebeple eğer Proc ile ana akışta bir return işlemi yaparsanız hata alacaksınızdır. Fakat lambda ile verdiğiniz değer geriye döndürülecektir.

# Should work
my_lambda = -> { return 1 }
puts "Lambda result: #{my_lambda.call}"

# Should raise exception
my_proc = Proc.new { return 1 }
puts "Proc result: #{my_proc.call}"

Özet Olarak Lambda Vs Proc

  1. Lambda -> {} ile tanımlanır — Proc ise Proc.new {} kullanır.
  2. Lambda değer döndürür — Proc bulunduğu methodu.
  3. Lamda gönderdiğiniz parametre sayıları yanlış ise hata döndürür — Proc ise parametre sayılarının doğruluğu ile ilgilenmez.

Karşılaştırmaya bakacak olursak Lambda method gibi davranırken Proc biraz daha farklı davranır

Ruby’ de Closure

Ruby’ de Lamda ve Proclar ile bir değişken bastırmak isterseniz bu yapılar her zaman tanımlandığı alandaki (scope) değişkenin değerini tutarlar ve değiştirmezler. Bu konsept Closure olarak adlandırılır.

def call_proc(my_proc)
count = 500
my_proc.call
end

count = 1
my_proc = Proc.new { puts count }
p call_proc(my_proc) # 1

Yukarıdaki sonuç 1 olacaktır. Çünkü Proc’ un tanımlandığı scoptaki count değeri 1' dir.

Örnek Kullanım:

def block_message_printer
message = "Welcome to Block Message Printer"
if block_given?
yield
end
puts "But in this function/method message is :: #{message}"
end

message = gets
block_message_printer { puts "This message remembers message :: #{message}" }

#####################################################################################

def proc_message_printer(my_proc)
message = "Welcome to Proc Message Printer"
my_proc.call #Call my_proc
puts "But in this function/method message is :: #{message}"
end


my_proc = proc { puts "This message remembers message :: #{message}" }
proc_message_printer(my_proc)

######################################################################################

def lambda_message_printer(my_lambda)
message = "Welcome to Lambda Message Printer"
my_lambda.call #Call my_lambda
puts "But in this function/method message is :: #{message}"
end

my_lambda = -> { puts "This message remembers message :: #{message}" }
lambda_message_printer(my_lambda)

######################################################################################

Bu örnekte ise method içerisinde çağırdığımız block, proc ve lambda yapıları mesaj olarak metodun içerisindeki mesajı değil de programın başında kullanıcıdan aldığımız yani block, proc ve lamda yapılarının tanımlandığı alandaki (scope) mesajı kullanacaktırlar.

Kaynakça:

--

--