Ruby'de hem proc hem lambda Proc sınıfının nesneleridir; aralarındaki fark davranıştadır. Lambda argüman sayısını metot gibi katı biçimde kontrol eder ve içindeki return yalnızca lambdadan çıkar. Normal proc ise eksik argümanları nil ile doldurur, fazlalarını atar, tek dizi argümanını parçalarına ayırır ve içindeki return onu çevreleyen metottan çıkar. Hangisi olduğunu lambda? metoduyla öğrenebilirsiniz.
Bu yazıda proc ve lambda oluşturmanın yollarını, iki türün argüman ve return farklarını, arity, curry, fonksiyon birleştirme ve &:sembol kısayolunun arkasındaki to_proc protokolünü anlatıyoruz. Örnekler Ruby 4.0 ile çalıştırıldı. Blokların temeli için önce blok ve yield yazısını okuyabilirsiniz.
Proc oluşturmanın yolları

Belgeler beş yol sayar: Proc.new, onun kısaltması olan Kernel#proc, bir metodun &block parametresiyle bloğu yakalamak, Kernel#lambda ve ->(x) { } biçimindeki lambda literal yazımı. İlk üçü normal proc, son ikisi lambda üretir. Bir proc'u çağırmak için call, .(), [] ya da yield kullanabilirsiniz; hepsi aynı işi yapar.
kare1 = Proc.new { |x| x * x }
kare2 = proc { |x| x * x }
kare3 = lambda { |x| x * x }
kare4 = ->(x) { x * x }
p kare1.call(3) # çıktı: 9
p kare2.(3) # çıktı: 9
p kare3[3] # çıktı: 9
p kare4.yield(3) # çıktı: 9
p [kare1, kare2, kare3, kare4].map(&:lambda?) # çıktı: [false, false, true, true]
p kare4.class # çıktı: ProcArgüman kontrolü: esnek proc, katı lambda
Lambdalarda argümanlar metotlardaki gibi işlenir: sayı tutmazsa ArgumentError oluşur ve ek bir dönüştürme yapılmaz. Normal proc'lar daha hoşgörülüdür. Belgelere göre eksik argümanlar nil olur, fazla argümanlar sessizce atılır ve birden fazla parametreli bir proc'a tek bir dizi verilirse dizi parçalarına ayrılır.
pr = proc { |a, b| [a, b] }
la = lambda { |a, b| [a, b] }
p pr.call(1) # çıktı: [1, nil]
p pr.call(1, 2, 3) # çıktı: [1, 2]
p pr.call([1, 2]) # çıktı: [1, 2]
p la.call(1, 2) # çıktı: [1, 2]
[[1], [1, 2, 3], [[1, 2]]].each do |args|
la.call(*args)
rescue ArgumentError => e
puts "#{e.class}: #{e.message}"
end
# çıktı: ArgumentError: wrong number of arguments (given 1, expected 2)
# çıktı: ArgumentError: wrong number of arguments (given 3, expected 2)
# çıktı: ArgumentError: wrong number of arguments (given 1, expected 2)Bu hoşgörü, belgelerin dediği gibi proc'ları iteratörler için kullanışlı yapar. map gibi metotlara verdiğiniz bloklar normal proc gibi davrandığı için [[1, 2], [3, 4]].map { |a, b| a + b } yazdığınızda iç diziler kendiliğinden a ve b'ye ayrılır. Aynı işi lambda ile yapmaya kalkarsanız map(&lambda) çağrısı ArgumentError verir, çünkü lambda niteliği & ile bloğa çevrildiğinde de korunur.
return ve break davranışı
En önemli fark burada. Lambda içindeki return ve break yalnızca lambdadan çıkar, tıpkı bir metottan dönmek gibi. Normal proc içindeki return ise proc'un tanımlandığı metottan çıkar. Belgelerdeki örneğin uyarlaması aşağıda: lambdanın return 3 satırı metodu durdurmaz, ama proc'un return 4 satırı metodu hemen bitirir.
def deneme
-> { return 3 }.call
proc { return 4 }.call
return 5
end
p deneme # çıktı: 4
def uret
proc { return 1 }
end
begin
uret.call
rescue LocalJumpError => e
puts "#{e.class}: #{e.message}" # çıktı: LocalJumpError: unexpected return
endİkinci örnekteki duruma belgeler "orphaned Proc" (sahipsiz proc) der: proc, onu oluşturan metot bittikten sonra hâlâ yaşıyorsa return çıkacak bir metot bulamaz ve LocalJumpError oluşur. break için de benzer bir durum geçerlidir. Lambdalar ise return ve break ile yalnızca kendilerinden çıktıkları için hiçbir zaman sahipsiz kalmaz. next ise her iki türde de yalnızca o çalıştırmayı bitirir.
arity ve parameters ile imzayı incelemek

arity, proc'un kaç zorunlu argüman beklediğini söyler. İsteğe bağlı argüman varsa sonuç negatif olur ve -n-1 biçiminde hesaplanır (n zorunlu argüman sayısıdır). Belgelere göre bir istisna var: lambda olmayan ve yalnızca sınırlı sayıda isteğe bağlı argümanı olan proc'larda sonuç doğrudan n olur. parameters ise her parametrenin türünü ve adını listeler; normal proc'ta zorunlu görünen parametreler bile :opt olarak raporlanır.
p proc { |a, b| }.arity # çıktı: 2
p lambda { |a, b| }.arity # çıktı: 2
p proc { |a, b = 0| }.arity # çıktı: 1
p lambda { |a, b = 0| }.arity # çıktı: -2
p proc { |*a| }.arity # çıktı: -1
p lambda { |x, y = 42, *diger| }.parameters
# çıktı: [[:req, :x], [:opt, :y], [:rest, :diger]]
p proc { |x, y = 42, *diger| }.parameters
# çıktı: [[:opt, :x], [:opt, :y], [:rest, :diger]]curry ve fonksiyon birleştirme
curry, argümanları parça parça alabilen bir proc döndürür. Yeterli sayıda argüman toplandığında asıl proc çalışır; toplanmadıysa geri kalanını bekleyen yeni bir curried proc döner. Değişken sayıda argüman alan proc'larda kaç argüman beklenmesi gerektiğini curry(n) ile belirtirsiniz. Lambdalarda bu sayı lambdanın imzasıyla uyumlu olmalıdır, aksi hâlde ArgumentError oluşur. >> ve << ise iki çağrılabilir nesneyi tek bir proc'ta birleştirir.
topla = ->(a, b, c) { a + b + c }
egri = topla.curry
p egri[1][2][3] # çıktı: 6
p egri[1, 2][3] # çıktı: 6
on_ekle = egri[10]
p on_ekle[1][2] # çıktı: 13
carp = proc { |x| x * 2 }
artir = proc { |x| x + 1 }
p (carp >> artir).call(5) # çıktı: 11
p (carp << artir).call(5) # çıktı: 12carp >> artir önce carp, sonra sonucu artir ile çalıştırır: (5 x 2) + 1 = 11. carp << artir sırayı tersine çevirir: (5 + 1) x 2 = 12. Belgelere göre birleştirilen taraf call metoduna cevap veren herhangi bir nesne olabilir, örneğin bir Method nesnesi.
to_proc ve &:sembol kısayolu
Çağrıda bir nesnenin önüne & koyduğunuzda Ruby o nesnenin to_proc metodunu çağırır ve sonucu blok olarak kullanır. Belgelere göre çekirdek sınıflardan Symbol, Method ve Hash bu metodu uygular. map(&:upcase) kısayolu bu yüzden çalışır: sembol, kendisini alan nesnede aynı adlı metodu çağıran bir proc'a dönüşür. Kendi sınıfınızda to_proc tanımlayarak aynı kolaylığı sağlayabilirsiniz.
p %w[elma armut].map(&:upcase) # çıktı: ["ELMA", "ARMUT"]
p :to_s.to_proc.call(42) # çıktı: "42"
[1, 2].each(&method(:puts))
# çıktı: 1
# çıktı: 2
fiyat = { elma: 30, armut: 45 }
p %i[elma kiraz armut].map(&fiyat) # çıktı: [30, nil, 45]
class Selam
def initialize(on) = @on = on
def to_proc = proc { |ad| "#{@on}, #{ad}!" }
end
p %w[Ali Ece].map(&Selam.new("Merhaba")) # çıktı: ["Merhaba, Ali!", "Merhaba, Ece!"]Sembol ve Hash'in diğer özellikleri için Symbol ve String farkı ile Hash metotları yazılarına bakabilirsiniz.
Closure: tanımlandığı ortamı hatırlamak
Proc ve lambda nesneleri closure'dır; oluşturuldukları andaki yerel değişkenleri hatırlar ve kullanmaya devam eder. Bir metodun döndürdüğü lambda, metot bittikten sonra bile metodun parametresine erişebilir. Dıştaki bir değişkeni değiştirirseniz bu değişiklik dışarıdan da görülür.
def carpan(n)
->(x) { x * n }
end
uc_kat = carpan(3)
p uc_kat.call(7) # çıktı: 21
n = 1
ekle = -> { n += 1 }
ekle.call
ekle.call
p n # çıktı: 3Ruby 3.3 notu: lambda(&proc) artık hata veriyor
Ruby 3.3 NEWS dosyasına göre Kernel#lambda metoduna literal olmayan, lambda niteliği taşımayan bir blok verilirse artık ArgumentError oluşuyor; bu kullanım Ruby 3.0'dan beri uyarı veriyordu. Yani mevcut bir proc'u lambda(&pr) ile lambdaya çeviremezsiniz. Ters yön ise sorunsuzdur: proc, kendisine verilen lambdanın niteliğini korur.
pr = proc { |x| x }
begin
lambda(&pr)
rescue ArgumentError => e
puts "#{e.class}: #{e.message}" # çıktı: ArgumentError: the lambda method requires a literal block
end
p proc(&lambda { |x| x }).lambda? # çıktı: trueProc ve lambda karşılaştırması

| Özellik | Normal proc | Lambda |
|---|---|---|
| Oluşturma | Proc.new, proc, &block | lambda, -> |
| lambda? | false | true |
| Eksik argüman | nil ile doldurulur | ArgumentError |
| Fazla argüman | Sessizce atılır | ArgumentError |
| Tek dizi argümanı | Parçalarına ayrılır | Tek argüman sayılır |
| return | Çevreleyen metottan çıkar | Yalnızca lambdadan çıkar |
| break | Bloğun verildiği metottan çıkar | Yalnızca lambdadan çıkar |
| Sahipsiz kalabilir mi | Evet, LocalJumpError | Hayır |
Sık yapılan hatalar
- Proc içinde return kullanıp metodun devam etmesini beklemek. Normal proc'taki
returnmetodu bitirir; yalnızca proc'tan çıkmak istiyorsanıznextya da lambda kullanın. - Metottan proc döndürüp içinde return bırakmak. Metot bittikten sonra çağrıldığında
LocalJumpErroralırsınız. - Lambdayı map'e & ile verip çift parametre beklemek. Lambda dizi argümanını parçalamaz; ya tek parametre alıp kendiniz ayırın ya da normal bir blok yazın.
- lambda(&mevcut_proc) ile dönüşüm denemek. Ruby 3.3'ten beri
ArgumentErrorverir. - arity değerini argüman sayısı sanmak. Negatif değerler isteğe bağlı argüman olduğunu gösterir; ayrıntı için
parametersçıktısına bakın.
Sık Sorulan Sorular
-> ile lambda arasında fark var mı?
Hayır, ikisi de lambda niteliğinde bir Proc üretir. ->(x) { } yalnızca daha kısa bir literal yazımdır.
Ne zaman proc, ne zaman lambda kullanmalıyım?
Belgeler lambdaları, metotlar gibi davrandıkları için kendi başına çalışan fonksiyonlar ve üst düzey fonksiyonlara verilecek argümanlar için uygun bulur. Proc'lar ise iteratör yazmak için, yani argümanların esnek işlenmesi ve return ile metottan çıkılması gereken yerlerde kullanışlıdır.
define_method ile tanımlanan metot proc gibi mi davranır?
Hayır. Belgelere göre define_method'a normal bir proc verseniz bile tanımlanan metot normal metot kurallarıyla argüman kontrolü yapar; bu, lambda niteliğinin korunmadığı tek istisnadır.
Blok parametrelerinde it ve _1 proc için de geçerli mi?
Evet, proc { it * 2 } ve lambda { _1 + _2 } yazılabilir. Ayrıntılar için blok parametreleri yazısına bakın.
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 · Blok ve yield · it ve _1 parametreleri · Metot tanımlama · Keyword argümanları · Symbol ve String farkı