Neler yeni

Yazılım Forum

Tüm özelliklerimize erişmek için şimdi bize katılın. Kayıt olduktan ve giriş yaptıktan sonra konu oluşturabilecek, mevcut konulara yanıt gönderebilecek, itibar kazanabilecek, özel mesajlaşmaya erişebilecek ve çok daha fazlasını yapabileceksiniz! Bu hizmetlerimiz ise tamamen ücretsiz ve kurallara uyulduğu sürece sınırsızdır, o zaman ne bekliyorsunuz? Hadi, sizde aramıza katılın!

Ruby Programlamasında Bloklar ve Proclar: Esneklik ve Fonksiyonel Yaklaşımlar

Ruby'de bloklar ve proclar, dilin en güçlü ve esnek özelliklerinden ikisidir. Bu yapılar, fonksiyonel programlama prensiplerini Ruby'ye taşıyarak, kodun daha modüler, okunabilir ve yeniden kullanılabilir olmasını sağlar. Bloklar, metodlara parametre olarak geçirilebilen anonim fonksiyon parçacıklarıyken, proclar bu blokları bir nesne olarak saklama ve daha sonra kullanma imkanı sunar. Ruby'nin metaprogramlama yeteneklerinin temelini oluşturan bu yapılar, özellikle döngüler, olay dinleyicileri (event listeners) ve özelleştirilmiş DSL'ler (Domain Specific Languages) oluşturulurken vazgeçilmezdir. Bu yazıda, Ruby'deki blokları ve procları derinlemesine inceleyecek, aralarındaki farkları, kullanım alanlarını ve en iyi pratikleri örneklerle açıklayacağız. Amacımız, bu güçlü araçları kullanarak daha esnek ve etkili Ruby kodları yazabilmeniz için kapsamlı bir rehber sunmaktır.

Bloklar:
Bloklar, Ruby'de metod çağrılarına eklenen anonim fonksiyonlardır. Genellikle bir döngünün her adımı için çalışacak kod parçalarını tanımlamak veya bir metoda özel davranışlar kazandırmak için kullanılırlar. Ruby'deki çoğu iterasyon metodu (örneğin
Kod:
each
,
Kod:
map
,
Kod:
select
) aslında bir blok alır.

Blok Tanımlama:
Bloklar iki farklı sentaks ile tanımlanabilir:
1. Tek satırlık kısa bloklar için
Kod:
{ ... }
süslü parantez kullanılır.
2. Çok satırlık bloklar için
Kod:
do ... end
anahtar kelimeleri kullanılır.

Örnekler:
Kod:
# Süslü parantez ile tek satırlık blok
[1, 2, 3].each { |num| puts num * 2 }

# do...end ile çok satırlık blok
[1, 2, 3].each do |num|
  result = num * 2
  puts "Sayı: #{num}, Sonuç: #{result}"
end

Metodlara Blok Geçirme ve
Kod:
yield
:

Bir metoda blok geçirdiğinizde, metodun içinde
Kod:
yield
anahtar kelimesini kullanarak bu bloğu çağırabilirsiniz.
Kod:
yield
, bloğun çalıştırılmasını sağlar ve metoda geçirilen argümanları bloğa iletebilir.

Kod:
def merhaba_blok
  puts "Merhaba, metodun içinden!"
  yield if block_given? # Eğer bir blok verilmişse çalıştır
  puts "Güle güle, metodun içinden!"
end

merhaba_blok do
  puts "Bu, metodun çağrılırken verilen bloktur."
end

puts "\n--- Başka bir örnek ---"

def islem_yap(a, b)
  if block_given?
    yield a, b
  else
    "Blok verilmedi."
  end
end

puts islem_yap(10, 5) { |x, y| x + y }
puts islem_yap(10, 5) { |x, y| x * y }
puts islem_yap(10, 5) # Blok verilmediği durum
Yukarıdaki örnekte,
Kod:
block_given?
metodu, çağrıya bir blok verilip verilmediğini kontrol eder. Bu, isteğe bağlı blok parametreleri için iyi bir pratiktir.

Blokların Kapsamı (Scope):
Bloklar, tanımlandıkları ortamdaki yerel değişkenlere erişebilirler. Bu özellik, closure olarak bilinir ve blokların tanımlandıkları zamanki çevreyi "kapatmaları" anlamına gelir.

Kod:
dis_degisken = "Merhaba Dünya!"

[1].each do
  puts dis_degisken # Blok dis_degisken'e erişebilir
end

def ornek_metod
  metod_degisken = "Metot İçi Değişken"
  # Burada metod_degisken'e erişilebilir
  yield if block_given?
end

ornek_metod do
  # puts metod_degisken # Bu hata verir, metod_degisken block'un scope'unda değil
  puts "Blok Çalıştı"
end
Bu örnek, blokların sadece kendi tanımlandıkları anki yerel kapsamdaki değişkenlere erişebildiğini, ancak kendilerini çağıran metodun yerel değişkenlerine doğrudan erişemediklerini gösterir (eğer o değişkenler bloğa parametre olarak geçirilmemişse).

Blok Parametreleri ve
Kod:
&block
:

Bir metot, son argüman olarak bir
Kod:
&block
parametresi alabilir. Bu, metoda geçirilen bloğu bir
Kod:
Proc
nesnesine dönüştürür ve bu Proc'u metodun içinde normal bir değişken gibi kullanmanıza olanak tanır.

Kod:
def proc_alan_metod(&arg_blok)
  puts "Proc'a dönüştürülmüş bloğu çağırıyorum..."
  arg_blok.call("Merhaba")
  puts "Proc'u tekrar çağırıyorum..."
  arg_blok.call("Dünya")
end

proc_alan_metod do |mesaj|
  puts "Blok içinden gelen mesaj: #{mesaj}"
end
Bu kullanım, bloğu bir Proc nesnesine dönüştürerek onu saklama, başka metodlara iletme veya birden çok kez çağırma esnekliği sağlar.

Proclar:
Proclar, blokların nesneleştirilmiş halidir. Bir bloğu bir
Kod:
Proc
nesnesine dönüştürdüğünüzde, onu bir değişkene atayabilir, başka metodlara argüman olarak geçirebilir, bir array veya hash içinde saklayabilir ve istediğiniz zaman çağırabilirsiniz. Bu, Ruby'de fonksiyonel programlama paradigmasını uygulamak için çok önemli bir araçtır.

Proc Oluşturma:
Proc oluşturmanın çeşitli yolları vardır:
1.
Kod:
Proc.new
: En temel yöntem. Bir blok alır ve onu bir Proc nesnesine dönüştürür.
2.
Kod:
lambda
:
Kod:
Proc.new
ile benzer, ancak bazı önemli farkları vardır (dönüş davranışı ve argüman kontrolü).
Kod:
->(args){ ... }
sentaksı da
Kod:
lambda
tanımlamak için kullanılır.
3.
Kod:
&
operatörü: Bir bloğu bir Proc'a dönüştürmek veya bir Proc'u bir bloğa dönüştürmek için kullanılır.

Örnekler:
Kod:
# Proc.new ile Proc oluşturma
my_proc = Proc.new { |isim| puts "Merhaba, #{isim}!" }
my_proc.call("Ahmet")
my_proc.call("Ayşe")

# lambda ile Proc oluşturma
my_lambda = lambda { |yas| puts "Yaşınız: #{yas}" }
my_lambda.call(30)

# -> sentaksı ile lambda
another_lambda = ->(sehir) { puts "Şehriniz: #{sehir}" }
another_lambda.call("İstanbul")

# & operatörü ile bloğu Proc'a dönüştürme
def blok_alinabilir(&b)
  b.call("Dünya")
end

blok_alinabilir do |arg|
  puts "Blok Proc'a dönüştürüldü: #{arg}"
end

Proc Çağırma:
Bir Proc nesnesini çağırmak için
Kod:
.call
,
Kod:
.()
veya
Kod:
.===
metodlarını kullanabilirsiniz.

Kod:
proc_ornek = Proc.new { |x, y| x + y }
puts proc_ornek.call(5, 3)  # => 8
puts proc_ornek.(10, 2)    # => 12
puts proc_ornek[7, 4]      # => 11

# Proc nesneleri, özellikle tek argüman alıyorlarsa, case ifadeleriyle birlikte de kullanılabilir.
# Bu durumda Proc'un === metodu çağrılır.
proc_kosul = Proc.new { |sayi| sayi.even? }
puts case 6
when proc_kosul
  "Çift sayı"
else
  "Tek sayı"
end # => "Çift sayı"

lambda_kosul = lambda { |metin| metin.include?("Ruby") }
puts case "Harika Ruby Kodu"
when lambda_kosul
  "Ruby içeren metin"
else
  "Ruby içermeyen metin"
end # => "Ruby içeren metin"

Proc ve Lambda Arasındaki Farklar:
Bu iki Proc oluşturma yöntemi arasındaki en önemli farklar şunlardır:


  • [li]Return Davranışı:
    Proc.new: Bir
    Kod:
    Proc.new
    içinde
    Kod:
    return
    kullanıldığında, sadece Proc'tan değil, Proc'un tanımlandığı metoddan da geri dönüş yapılır (non-local return). Eğer Proc'un tanımlandığı metoddan zaten çıkılmışsa, bir
    Kod:
    LocalJumpError
    fırlatılır.
    Lambda: Bir
    Kod:
    lambda
    içinde
    Kod:
    return
    kullanıldığında, sadece lambdadan geri dönüş yapılır. Davranış normal bir metoddan
    Kod:
    return
    yapmaya benzer (local return).
    Kod:
    def proc_ornek_metod_duzgun
      my_proc = Proc.new { return "Proc'tan döndüm" }
      sonuc = my_proc.call # Bu çağrı, metodu tamamen terk eder.
      "Proc çağrıldıktan sonraki metin: #{sonuc}"
    rescue LocalJumpError => e
      "LocalJumpError: #{e.message}"
    end
    
    def lambda_ornek_metod_duzgun
      my_lambda = lambda { return "Lambda'dan döndüm" }
      sonuc = my_lambda.call # Lambda kendi içinde döner, metod devam eder.
      "Lambda çağrıldıktan sonraki metin: #{sonuc}"
    end
    
    puts "Proc sonucu: #{proc_ornek_metod_duzgun}"
    puts "Lambda sonucu: #{lambda_ornek_metod_duzgun}"
    [li]Argüman Kontrolü (Arity):
    Proc.new: Argüman sayısını esnek karşılar. Eksik veya fazla argüman verilse bile hata fırlatmaz, eksikleri
    Kod:
    nil
    ile doldurur, fazlaları yok sayar.
    Lambda: Argüman sayısını katı bir şekilde kontrol eder. Beklenenden fazla veya eksik argüman verilirse
    Kod:
    ArgumentError
    hatası fırlatır. Bu davranış, normal metotlara benzer.
    Kod:
    proc_arity = Proc.new { |a, b| "Proc: #{a}, #{b}" }
    puts proc_arity.call(1)       # => "Proc: 1, " (b nil olur)
    puts proc_arity.call(1, 2, 3) # => "Proc: 1, 2" (3 yok sayılır)
    
    lambda_arity = lambda { |a, b| "Lambda: #{a}, #{b}" }
    puts lambda_arity.call(1, 2)  # => "Lambda: 1, 2"
    # puts lambda_arity.call(1)   # => ArgumentError: wrong number of arguments (given 1, expected 2)
    # puts lambda_arity.call(1, 2, 3) # => ArgumentError: wrong number of arguments (given 3, expected 2)

Ne Zaman Hangisini Kullanmalı?
Genel olarak, seçim, yazdığınız kodun bağlamına ve istediğiniz davranışa bağlıdır:

  • [li]Bloklar:

    • [li]Bir metoda tek seferlik, isimsiz bir fonksiyonelliği geçirmek istediğinizde.[/li]
      [li]Kısa, anonim iterasyonlar veya callback'ler için (örn.
      Kod:
      each
      ,
      Kod:
      map
      ).[/li]
      [li]Kodun okunaklığını artırmak için (doğrudan metoda eklenmiş gibi hissettirir).[/li]
    [/li]
    [li]Proclar (Genel):

    • [li]Bir bloğu bir değişken olarak saklamak ve daha sonra tekrar kullanmak istediğinizde.[/li]
      [li]Bir bloğu birden fazla metoda argüman olarak geçirmek veya bir veri yapısı içinde depolamak istediğinizde.[/li]
      [li]Özelleştirilmiş kontrol yapıları veya DSL'ler oluştururken.[/li]
    [/li]
    [li]Lambdalar (Proc'un Katı Versiyonu):

    • [li]Davranışı normal metodlara daha çok benzeyen (katı argüman kontrolü, yerel dönüş) bir callback veya fonksiyonel nesneye ihtiyacınız olduğunda.[/li]
      [li]Genellikle API'ler tasarlarken, metodlara geçireceğiniz callback'lerin belirli bir arayüze uymasını sağlamak için tercih edilir.[/li]
    [/li]

Gelişmiş Kullanım Alanları ve Örnekler:

1. Callback Fonksiyonları:
Proclar, özellikle olay tabanlı sistemlerde veya kancalar (hooks) oluşturmada callback olarak kullanılabilir.

Kod:
class VeriIsleyici
  def initialize(&callback)
    @callback = callback
  end

  def veriyi_isleme_baslat(veri)
    puts "Veri işleniyor: #{veri}"
    if @callback && @callback.is_a?(Proc)
      @callback.call(veri.upcase) # İşlenmiş veriyi callback'e gönder
    else
      puts "Callback tanımlanmadı veya geçerli değil."
    end
  end
end

# Bir lambda ile callback tanımla
rapor_olusturucu = lambda do |islenmis_veri|
  puts "RAPOR: #{islenmis_veri} işlendi ve raporlandı."
end

veri_nesnesi = VeriIsleyici.new(&rapor_olusturucu)
veri_nesnesi.veriyi_isleme_baslat("urun_a")

# Bir Proc.new ile callback tanımla
log_yazici = Proc.new do |islenmis_veri|
  puts "LOG: #{Time.now}: #{islenmis_veri} kaydedildi."
end

veri_nesnesi_2 = VeriIsleyici.new(&log_yazici)
veri_nesnesi_2.veriyi_isleme_baslat("musteri_bilgisi")

2. Domain Specific Languages (DSLs) Oluşturma:
Ruby'nin bloklar ve proclar üzerindeki esnekliği, Rake, Thor veya Rails'ın yönlendirme (routing) gibi birçok ünlü kütüphanenin kendine özgü DSL'ler oluşturmasına olanak tanır.

Kod:
# Basit bir DSL örneği: Rapor Tanımlama
class Raporleyici
  def self.rapor_tanimla(isim, &block)
    puts "Yeni rapor tanımlanıyor: '#{isim}'"
    @current_report_name = isim
    instance_eval(&block) # Bloku raporleyici sınıfının bağlamında çalıştır
    puts "Rapor '#{isim}' tanımlandı."
  end

  def self.baslik(text)
    puts "  Başlık: #{text}"
  end

  def self.veri_kaynagi(kaynak)
    puts "  Veri Kaynağı: #{kaynak}"
  end

  def self.kolon_ekle(kolon_adi)
    puts "  Kolon Eklendi: #{kolon_adi}"
  end
end

Raporleyici.rapor_tanimla "Aylık Satış Raporu" do
  baslik "Şirket Aylık Satış Özeti"
  veri_kaynagi "satistik_db"
  kolon_ekle :urun_adi
  kolon_ekle :satis_miktari
  kolon_ekle :toplam_gelir
end

Raporleyici.rapor_tanimla "Müşteri Bilgileri" do
  baslik "Kayıtlı Müşteriler Listesi"
  veri_kaynagi "crm_db"
  kolon_ekle :musteri_id
  kolon_ekle :ad_soyad
  kolon_ekle :email
end
Bu örnekte,
Kod:
instance_eval(&block)
kullanımı, bloğun
Kod:
Raporleyici
sınıfının bağlamında çalışmasını sağlayarak
Kod:
baslik
,
Kod:
veri_kaynagi
gibi metodları doğrudan çağırmamıza olanak tanır.

3. Memoization (Belleğe Alma):
Proclar, özellikle hesaplaması pahalı olan metodların sonuçlarını önbelleğe almak için kullanılabilir.

Kod:
class Hesaplayici
  def initialize
    @cache = {}
  end

  def pahali_hesap(key, &block)
    @cache[key] ||= block.call
  end
end

calc = Hesaplayici.new

# İlk çağrı: Hesaplama yapılır
sonuc1 = calc.pahali_hesap("buyuk_sayi") do
  puts "Çok pahalı bir hesaplama yapılıyor..."
  sleep 0.1 # Simüle edilmiş gecikme
  12345 * 98765
end
puts "Sonuç 1: #{sonuc1}"

# İkinci çağrı: Önbellekten alınır, blok tekrar çalışmaz
sonuc2 = calc.pahali_hesap("buyuk_sayi") do
  puts "Bu satırı görmemelisiniz!"
  0 # Bu değer hiç kullanılmaz
end
puts "Sonuç 2: #{sonuc2}"

puts "\nBaşka bir hesaplama:"
sonuc3 = calc.pahali_hesap("kucuk_sayi") do
  puts "Yeni bir hesaplama yapılıyor..."
  5 * 5
end
puts "Sonuç 3: #{sonuc3}"

Sonuç:
Ruby'deki bloklar ve proclar, dilin dinamik ve esnek yapısının temel taşlarındandır. Anonim fonksiyonlar olan bloklar, metodlara geçici kod parçacıkları eklemek için idealdir. Proclar ise, bu blokları birinci sınıf nesneler olarak ele almamızı sağlayarak, onları saklama, yeniden kullanma ve metodlar arasında geçirme yeteneği sunar. Lambda ve Proc.new arasındaki dönüş davranışı ve argüman kontrolü farklılıklarını anlamak, doğru aracı doğru yerde kullanmak için kritik öneme sahiptir. Bu yapılar, Ruby geliştiricilerine güçlü metaprogramlama yetenekleri, özelleştirilmiş kontrol akışları ve temiz, modüler kod yazma imkanı sunar. Bu konuyu daha derinlemesine incelemek için Ruby resmi dokümantasyonunu ziyaret edebilirsiniz. Unutmayın, bu yapıları ustaca kullanmak, Ruby'de daha verimli ve şık çözümler üretmenizi sağlayacaktır.
 
shape1
shape2
shape3
shape4
shape5
shape6
Üst

Bu web sitenin performansı Hazal Host tarafından sağlanmaktadır.

YazilimForum.com.tr internet sitesi, 5651 sayılı Kanun’un 2. maddesinin 1. fıkrasının (m) bendi ve aynı Kanun’un 5. maddesi kapsamında Yer Sağlayıcı konumundadır. Sitede yer alan içerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır.

YazilimForum.com.tr, kullanıcılar tarafından paylaşılan içeriklerin doğruluğunu, güncelliğini veya hukuka uygunluğunu garanti etmez ve içeriklerin kontrolü veya araştırılması ile yükümlü değildir. Kullanıcılar, paylaştıkları içeriklerden tamamen kendileri sorumludur.

Hukuka aykırı içerikleri fark ettiğinizde lütfen bize bildirin: lydexcoding@gmail.com

Sitemiz, kullanıcıların paylaştığı içerik ve bilgileri 6698 sayılı KVKK kapsamında işlemektedir. Kullanıcılar, kişisel verileriyle ilgili haklarını KVKK Politikası sayfasından inceleyebilir.

Sitede yer alan reklamlar veya üçüncü taraf bağlantılar için YazilimForum.com.tr herhangi bir sorumluluk kabul etmez.

Sitemizi kullanarak Forum Kuralları’nı kabul etmiş sayılırsınız.

DMCA.com Protection Status Copyrighted.com Registered & Protected