Ruby'de bir proc ve lambda arasındaki fark nedir?


Yanıtlar:


260

Farklardan biri, argümanları ele alma biçimidir. Kullanarak bir proc oluşturma proc {}ve Proc.new {}eşdeğerdir. Ancak, kullanmak lambda {}size iletilen bağımsız değişken sayısını kontrol eden bir proc sağlar. Gönderen ri Kernel#lambda:

Eşdeğer Proc.new elde edilen Proc hariç çağrıldığında geçen parametre sayısını kontrol nesneleri.

Bir örnek:

p = Proc.new {|a, b| puts a**2+b**2 } # => #<Proc:0x3c7d28@(irb):1>
p.call 1, 2 # => 5
p.call 1 # => NoMethodError: undefined method `**' for nil:NilClass
p.call 1, 2, 3 # => 5
l = lambda {|a, b| puts a**2+b**2 } # => #<Proc:0x15016c@(irb):5 (lambda)>
l.call 1, 2 # => 5
l.call 1 # => ArgumentError: wrong number of arguments (1 for 2)
l.call 1, 2, 3 # => ArgumentError: wrong number of arguments (3 for 2)

Buna ek olarak, Ken'nin işaret ettiği gibi, returnbir lambda içinde kullanmak bu lambda'nın değerini döndürür, ancak returnbir proc'da kullanmak kapalı bloktan döner.

lambda { return :foo }.call # => :foo
return # => LocalJumpError: unexpected return
Proc.new { return :foo }.call # => LocalJumpError: unexpected return

Bu nedenle, çoğu hızlı kullanım için aynıdırlar, ancak otomatik katı bağımsız değişken denetimi (bazen hata ayıklamaya da yardımcı olabilir) veya returnproc'un değerini döndürmek için ifadeyi kullanmanız gerekiyorsa kullanın lambda.


8
Protezler bloklara çok benzerken (argümanlar kontrol edilmez ve içerme yönteminden veya lambda'dan bir dönüş döner), lambdaların yöntemlere çok benzediğini (argümanları kontrol edin ve onlardan geri dönecek) söylemek doğru olur mu?
pedz

Şimdiye kadar kaç web sitesi ve makalenin Tanrı'ya gittiğini biliyordum ve hiç kimse Procs'a karşı yöntemlerin lambdalara karşı yararından bahsetmiyor gibi görünüyor. Her açıklama, dönüş değerlerinin vb. Nasıl farklı olduğuna dair saç kesen bir ayrıntı sağlar, ancak neden önemli olduğuna dair hiçbir şey yoktur. Şimdilik bunun Ruby'de bir tasarım karmaşası olduğu sonucuna varmalıyım.
ankush981

76

Procs ve lambdas arasındaki gerçek fark, kontrol akışı anahtar kelimeleriyle ilgilidir. Ancak bahsettiğimiz return, raise, break, redo, retrybu kontrol kelimeleri - vs.. Diyelim ki bir süreçte iade beyanınız var. Proc'unuzu çağırdığınızda, sadece sizi dışarı atmaz, aynı zamanda ek yöntemden de geri döner:

def my_method
  puts "before proc"
  my_proc = Proc.new do
    puts "inside proc"
    return
  end
  my_proc.call
  puts "after proc"
end

my_method

shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc

putsYöntemin finali asla yürütülmedi, çünkü proc'umuzu çağırdığımızda, içerideki returnyöntemden bizi terk etti. Bununla birlikte, proc'umuzu bir lambda'ya dönüştürürsek, aşağıdakileri elde ederiz:

def my_method
  puts "before proc"
  my_proc = lambda do
    puts "inside proc"
    return
  end
  my_proc.call
  puts "after proc"
end

my_method
shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc
after proc

Lambda içindeki dönüş bizi sadece lambda'nın dışına atar ve kapatma yöntemi çalışmaya devam eder. Kontrol akışı anahtar kelimelerinin proc ve lambdalar içinde işlenme şekli aralarındaki temel farktır


7

Sadece iki ana fark vardır.

  • İlk olarak, bir, kendisine lambdailetilen bağımsız değişkenlerin sayısını denetlerken , a procdeğil. Bu, a lambdaifadesinin yanlış sayıda argüman iletmeniz durumunda bir hata atar, oysa a procbeklenmeyen argümanları yoksayar ve nileksik olanlara atar .
  • İkincisi, bir lambdageri döndüğünde kontrolü çağıran yönteme geri aktarır; bir procgeri döndüğünde, arama yöntemine geri dönmeden bunu hemen yapar.

Bunun nasıl çalıştığını görmek için aşağıdaki koda bir göz atın. İlk yöntemimiz proc; ikinci çağrı a lambda.

def batman_ironman_proc
  victor = Proc.new { return "Batman will win!" }
  victor.call
  "Iron Man will win!"
end

puts batman_ironman_proc # prints "Batman will win!"

def batman_ironman_lambda
  victor = lambda { return "Batman will win!" }
  victor.call
  "Iron Man will win!"
end

puts batman_ironman_lambda # prints "Iron Man will win!"

Bakın proc"Batman kazanacak!" Diyor, bunun nedeni batman_ironman_proc yöntemine geri dönmeden hemen geri dönmesidir.

lambdaAncak bizim çağrıldıktan sonra yönteme geri döner, böylece yöntem değerlendirdiği son kodu döndürür: "Iron Man kazanacak!"


5

# Proc Örnekleri

p = Proc.new { |x| puts x*2 }
[1,2,3].each(&p)              # The '&' tells ruby to turn the proc into a block 

proc = Proc.new { puts "Hello World" }
proc.call

# Lambda Örnekleri

lam = lambda { |x| puts x*2 }
[1,2,3].each(&lam)

lam = lambda { puts "Hello World" }
lam.call           

Procs ve Lambdas arasındaki farklar

Procs ve lambdalar arasındaki farklara girmeden önce, her ikisinin de Proc nesnesi olduğunu belirtmek önemlidir.

proc = Proc.new { puts "Hello world" }
lam = lambda { puts "Hello World" }

proc.class # returns 'Proc'
lam.class  # returns 'Proc'

Ancak lambdalar, farklı bir procs lezzetidir. Bu küçük fark, nesneleri döndürürken gösterilir.

proc   # returns '#<Proc:0x007f96b1032d30@(irb):75>'
lam    # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'

1. Lambdas argüman sayısını kontrol ederken proclar

lam = lambda { |x| puts x }    # creates a lambda that takes 1 argument
lam.call(2)                    # prints out 2
lam.call                       # ArgumentError: wrong number of arguments (0 for 1)
lam.call(1,2,3)                # ArgumentError: wrong number of arguments (3 for 1)

Aksine, procslar yanlış sayıda argümanın geçip geçmediği umurumda değil.

proc = Proc.new { |x| puts x } # creates a proc that takes 1 argument
proc.call(2)                   # prints out 2
proc.call                      # returns nil
proc.call(1,2,3)               # prints out 1 and forgets about the extra arguments

2. Lambdas ve procs 'return' anahtar kelimesine farklı davranıyor

Bir lambda'nın içindeki 'dönüş' kodu, lambda kodunun hemen dışındaki kodu tetikler

def lambda_test
  lam = lambda { return }
  lam.call
  puts "Hello world"
end

lambda_test                 # calling lambda_test prints 'Hello World'

Bir proc'un içindeki 'return', proc'un yürütüldüğü yöntemin dışındaki kodu tetikler

def proc_test
  proc = Proc.new { return }
  proc.call
  puts "Hello world"
end

proc_test                 # calling proc_test prints nothing

Ve diğer sorgunuzu cevaplamak için hangisini ne zaman kullanmalı? Bahsettiği gibi @jtbandes'i takip edeceğim

Bu nedenle, çoğu hızlı kullanım için aynıdırlar, ancak otomatik katı bağımsız değişken denetimi istiyorsanız (bu da bazen hata ayıklamaya yardımcı olabilir) veya proc değerini döndürmek için return deyimini kullanmanız gerekiyorsa lambda kullanın.

Başlangıçta buraya gönderildi


1

Genel olarak konuşursak, lambdalar prokilerden daha sezgiseldir çünkü yöntemlere daha benzerler. Arity konusunda oldukça katılar ve dönüşü çağırdığınızda çıkarlar. Bu nedenle, birçok Rubyist, procların belirli özelliklerine ihtiyaç duymadıkları sürece lambdas'ı ilk seçenek olarak kullanır.

Procs: Sınıf nesneleri Proc. Bloklar gibi, tanımlandıkları kapsamda değerlendirilirler. Lambdas: Ayrıca sınıftaki nesneler, Procama normal süreçlerden oldukça farklı. Bloklar ve proclar gibi kapaklardır ve bu nedenle tanımlandıkları kapsamda değerlendirilirler.

Proc Oluşturma

a = Proc.new { |x| x 2 }

Lambda oluşturma

b = lambda { |x| x 2 }


a = proc { |x| x 2 }ile aynıa = Proc.new { |x| x 2 }
lacostenycoder

1

İşte bunu anlamanın başka bir yolu.

Blok, bir nesne üzerindeki bir yöntemin çağrılması için çağrılan bir kod dizisidir. Aşağıdaki örnekte self, Rails çerçevesinde ActionView :: Base'den devralınan anonim bir sınıfın bir örneğidir (kendisi birçok yardımcı modülü içerir). Kart, kendi üzerine dediğimiz bir yöntemdir. Yönteme bir argüman iletiriz ve sonra bloğu her zaman yöntem çağrısının sonuna ekleriz:

self.card :contacts do |c|
  // a chunk of valid ruby code    
end

Pekala, bir yönteme kod yığınını geçiriyoruz. Fakat bu bloğu nasıl kullanacağız? Bir seçenek, kod yığınını bir nesneye dönüştürmektir. Ruby, bir kod parçasını bir nesneye dönüştürmenin üç yolunu sunar

# lambda
> l = lambda { |a| a + 1 }
> l.call(1)
=> 2 

# Proc.new
> l2= Proc.new { |a| a + 1 }
> l2.call(1)
=> 2 

# & as the last method argument with a local variable name
def add(&block)
end

Yukarıdaki yöntemde, & yönteme iletilen bloğu bir nesneye dönüştürür ve o nesneyi yerel değişken bloğunda saklar. Aslında, lambda ve Proc ile aynı davranışa sahip olduğunu gösterebiliriz. Yeni:

def add(&block)
  block
end

l3 = add { |a| a + 1 }
l3.call(1)
=> 2

Bu önemli. Bir bloğu bir yönteme geçirip & öğesini kullanarak dönüştürdüğünüzde, oluşturduğu nesne dönüştürme işlemi için Proc.new öğesini kullanır.

Ben bir seçenek olarak "proc" kullanmaktan kaçındım unutmayın. Çünkü Ruby 1.8, lambda ile aynıdır ve Ruby 1.9'da Proc.new ile aynıdır ve tüm Ruby versiyonlarında kaçınılmalıdır.

Öyleyse lambda ve Proc.new arasındaki farkın ne olduğunu soruyorsunuz?

İlk olarak, parametre geçişi açısından lambda bir yöntem çağrısı gibi davranır. Yanlış sayıda argüman iletirseniz bir istisna ortaya çıkar. Aksine, Proc.new paralel atama gibi davranır. Kullanılmayan tüm argümanlar nil değerine dönüştürülür:

> l = lambda {|a,b| puts "#{a} + #{b}" }
 => #<Proc:0x007fbffcb47e40@(irb):19 (lambda)> 
> l.call(1)
ArgumentError: wrong number of arguments (1 for 2)

> l2 = Proc.new {|a,b| puts "#{a} + #{b}" }
=> #<Proc:0x007fbffcb261a0@(irb):21> 
> l2.call(1)
1 + 

İkincisi, lambda ve Proc.new, return anahtar sözcüğünü farklı şekilde ele alır. Proc.new içinde bir dönüş yaptığınızda, aslında çevreleme yönteminden, yani çevreleyen bağlamdan döner. Bir lambda bloğundan döndüğünüzde, kapalı yöntemden değil, yalnızca bloktan döner. Temel olarak, çağrıdan bloğa çıkar ve kapatma yönteminin geri kalanıyla yürütmeye devam eder.

> def add(a,b)
  l = Proc.new { return a + b}
  l.call
  puts "now exiting method"
end
> add(1,1)
=> 2  # NOTICE it never prints the message "now exiting method"

> def add(a,b)
  l = lambda { return a + b }
  l.call
  puts "now exiting method"
end
> add(1,1)
=> now exiting method  # NOTICE this time it prints the message "now exiting method"

Öyleyse neden bu davranış farkı? Bunun nedeni Proc.new ile yineleyicileri kapalı yöntemler bağlamında kullanabilir ve mantıksal sonuçlar çıkarabiliriz. Şu örneğe bakın:

> def print(max)
  [1,2,3,4,5].each do |val|
    puts val
    return if val > max
  end
end
> print(3)
1
2
3
4

Yineleyicinin içindeki dönüşü çağırdığımızda, çevirme yönteminden döneceğini umuyoruz. Yineleyicilere iletilen blokların Proc.new kullanarak nesnelere dönüştürüldüğünü ve bu nedenle return kullandığımızda, kapatma yönteminden çıkacağını unutmayın.

Lambdaları anonim yöntemler olarak düşünebilirsiniz, tek tek kod bloklarını bir yöntem gibi ele alınabilecek bir nesneye ayırırlar. Sonuçta, bir lambda'yı anomyous bir yöntem olarak ve Proc. Yeni satır içi kod olarak davranmayı düşünün.


0

Ruby kılavuzları hakkında yararlı bir yazı: bloklar, procs ve lambdas

Protezler mevcut yöntemden, lambdalar ise lambda'nın kendisinden döner.

Protezler doğru sayıda argümanı umursamazken, lambdalar bir istisna doğuracak.


-3

proc ve lambda arasındaki farklar, proc'un argümanların değiştirildiği kodun sadece bir kopyası, lambda diğer dillerde olduğu gibi bir işlev olmasıdır. (dönüş davranışı, argüman kontrolleri)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.