Apa perbedaan antara proc dan lambda di Ruby?

Jawaban:

260

Salah satu perbedaan adalah cara mereka menangani argumen. Membuat proc menggunakan proc {}dan Proc.new {}setara. Namun, menggunakan lambda {}memberi Anda proc yang memeriksa jumlah argumen yang diteruskan ke sana. Dari ri Kernel#lambda:

Setara dengan Proc.new , kecuali objek Proc yang dihasilkan memeriksa jumlah parameter yang dilewati saat dipanggil.

Sebuah contoh:

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)

Selain itu, seperti yang ditunjukkan Ken, menggunakan returndi dalam lambda mengembalikan nilai lambda itu, tetapi menggunakan returndalam proc kembali dari blok penutup.

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

Jadi untuk sebagian besar penggunaan cepat, keduanya sama, tetapi jika Anda ingin memeriksa argumen ketat secara otomatis (yang kadang-kadang juga dapat membantu dengan debugging), atau jika Anda perlu menggunakan returnpernyataan untuk mengembalikan nilai dari proc, gunakan lambda.

jtbandes
sumber
8
Apakah akurat untuk mengatakan bahwa lambda sangat mirip metode (periksa argumen dan pengembalian akan kembali dari mereka) sementara procs sangat mirip blok (argumen tidak dicentang dan pengembalian akan kembali dari metode yang mengandung atau lambda)?
pedz
Saya telah ke Tuhan tahu berapa banyak situs web dan artikel sekarang dan tampaknya tidak ada yang berbicara tentang kegunaan Procs vs metode vs lambdas. Setiap penjelasan hanya memberikan perincian yang memecah-belah tentang bagaimana nilai-nilai pengembalian, dll. Berbeda, tetapi tidak ada mengapa itu penting. Untuk saat ini saya harus menyimpulkan bahwa ini adalah kekacauan desain di Ruby.
ankush981
76

Perbedaan nyata antara procs dan lambdas ada hubungannya dengan kontrol kata kunci aliran. Saya berbicara tentang return, raise, break, redo, retrydll - kata-kata kontrol. Katakanlah Anda memiliki pernyataan pengembalian dalam sebuah proc. Ketika Anda memanggil proc Anda, itu tidak hanya akan membuang Anda keluar dari itu, tetapi juga akan kembali dari metode terlampir misalnya:

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

Final putsdalam metode, tidak pernah dieksekusi, karena ketika kami memanggil proc kami, bagian returndalamnya membuang kami keluar dari metode. Namun, jika kami mengubah proc kami menjadi lambda, kami mendapatkan yang berikut:

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

Pengembalian dalam lambda hanya membuang kita keluar dari lambda itu sendiri dan metode terlampir terus dijalankan. Cara kontrol aliran kata kunci diperlakukan dalam procs dan lambdas adalah perbedaan utama di antara mereka

Shoaib
sumber
7

Hanya ada dua perbedaan utama.

  • Pertama, a lambdamemeriksa jumlah argumen yang diteruskan ke sana, sementara a proctidak. Ini berarti bahwa a lambdaakan melempar kesalahan jika Anda memberikannya jumlah argumen yang salah, sedangkan a procakan mengabaikan argumen yang tidak terduga dan menugaskan nilsiapa pun yang hilang.
  • Kedua, ketika lambdakembali, ia melewati kontrol kembali ke metode pemanggilan; ketika prockembali, ia melakukannya segera, tanpa kembali ke metode panggilan.

Untuk melihat cara kerjanya, lihat kode di bawah ini. Metode pertama kami memanggil a proc; panggilan kedua 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!"

Lihat bagaimana prockata "Batman akan menang!", Ini karena ia segera kembali, tanpa kembali ke metode batman_ironman_proc.

Kami lambda, bagaimanapun, kembali ke metode setelah dipanggil, sehingga metode mengembalikan kode terakhir mengevaluasi: "! Iron Man akan menang"

Rajkaran Mishra
sumber
5

# Contoh Proc

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

# Contoh Lambda

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

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

Perbedaan antara Procs dan Lambdas

Sebelum saya membahas perbedaan antara procs dan lambdas, penting untuk menyebutkan bahwa keduanya adalah objek Proc.

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

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

Namun, lambda adalah 'rasa' procs yang berbeda. Perbedaan kecil ini ditunjukkan ketika mengembalikan objek.

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

1. Lambdas memeriksa jumlah argumen, sementara procs tidak

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)

Sebaliknya, procs tidak peduli jika mereka melewati jumlah argumen yang salah.

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 dan procs memperlakukan kata kunci 'kembali' secara berbeda

'kembali' di dalam lambda memicu kode tepat di luar kode lambda

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

lambda_test                 # calling lambda_test prints 'Hello World'

'kembali' di dalam proc memicu kode di luar metode di mana proc dieksekusi

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

proc_test                 # calling proc_test prints nothing

Dan untuk menjawab pertanyaan Anda yang lain, mana yang harus digunakan dan kapan? Saya akan mengikuti @jtbandes seperti yang dia sebutkan

Jadi untuk sebagian besar penggunaan cepat, keduanya sama, tetapi jika Anda ingin memeriksa argumen ketat secara otomatis (yang terkadang juga dapat membantu debugging), atau jika Anda perlu menggunakan pernyataan pengembalian untuk mengembalikan nilai dari proc, gunakan lambda.

Awalnya diposting di sini

Arvind singh
sumber
1

Secara umum, lambda lebih intuitif daripada procs karena mereka lebih mirip dengan metode. Mereka cukup ketat tentang arity, dan mereka hanya keluar ketika Anda menelepon kembali. Untuk alasan ini, banyak Rubyists menggunakan lambdas sebagai pilihan pertama, kecuali mereka membutuhkan fitur khusus procs.

Procs: Objek kelas Proc. Seperti blok, mereka dievaluasi dalam lingkup di mana mereka didefinisikan. Lambdas: Juga objek kelas Proctetapi sedikit berbeda dari procs biasa. Mereka penutupan seperti blok dan procs, dan dengan demikian mereka dievaluasi dalam lingkup di mana mereka didefinisikan.

Membuat Proc

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

Menciptakan lambda

b = lambda { |x| x 2 }

spirito_libero
sumber
a = proc { |x| x 2 }sama dengana = Proc.new { |x| x 2 }
lacostenycoder
1

Berikut ini cara lain untuk memahami hal ini.

Blok adalah bongkahan kode yang dilampirkan pada pemanggilan panggilan metode pada suatu objek. Dalam contoh di bawah ini, self adalah turunan dari kelas anonim yang mewarisi dari ActionView :: Base dalam kerangka Rails (yang itu sendiri mencakup banyak modul pembantu). kartu adalah metode yang kita sebut sendiri. Kami menyampaikan argumen ke metode dan kemudian kami selalu melampirkan blok ke akhir doa metode:

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

Oke, jadi kami mengirimkan sepotong kode ke sebuah metode. Tetapi bagaimana kita memanfaatkan blok ini? Salah satu opsi adalah mengubah potongan kode menjadi objek. Ruby menawarkan tiga cara untuk mengubah sepotong kode menjadi objek

# 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

Dalam metode di atas, & mengubah blok yang diteruskan ke metode menjadi objek dan menyimpan objek itu di blok variabel lokal. Bahkan, kami dapat menunjukkan bahwa ia memiliki perilaku yang sama dengan lambda dan Proc.new:

def add(&block)
  block
end

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

Ini penting. Saat Anda meneruskan blok ke metode dan mengonversinya menggunakan &, objek yang dibuatnya menggunakan Proc.new untuk melakukan konversi.

Perhatikan bahwa saya menghindari penggunaan "proc" sebagai opsi. Itu karena Ruby 1.8, sama dengan lambda dan di Ruby 1.9, itu sama dengan Proc.new dan di semua versi Ruby itu harus dihindari.

Jadi, Anda bertanya apa perbedaan antara lambda dan Proc.new?

Pertama, dalam hal melewati parameter, lambda berperilaku seperti pemanggilan metode. Ini akan memunculkan pengecualian jika Anda melewatkan jumlah argumen yang salah. Sebaliknya, Proc.new berperilaku seperti penugasan paralel. Semua argumen yang tidak digunakan dikonversi menjadi nihil:

> 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 + 

Kedua, lambda dan Proc.new menangani kata kunci kembali secara berbeda. Ketika Anda melakukan pengembalian di dalam Proc.new, itu sebenarnya kembali dari metode melampirkan, yaitu konteks sekitarnya. Ketika Anda kembali dari blok lambda, itu hanya kembali dari blok, bukan metode melampirkan. Pada dasarnya, ia keluar dari panggilan ke blok dan melanjutkan eksekusi dengan sisa metode terlampir.

> 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"

Lantas mengapa perbedaan perilaku ini? Alasannya adalah karena dengan Proc.new baru, kita dapat menggunakan iterator dalam konteks melampirkan metode dan menarik kesimpulan logis. Lihatlah contoh ini:

> 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

Kami berharap bahwa ketika kami meminta return di dalam iterator, itu akan kembali dari metode terlampir. Ingat blok yang dilewatkan ke iterator dapat dikonversi ke objek menggunakan Proc.new dan itulah sebabnya ketika kita menggunakan return, itu akan keluar dari metode melampirkan.

Anda dapat menganggap lambdas sebagai metode anonim, mereka mengisolasi blok kode individu menjadi objek yang dapat diperlakukan seperti metode. Pada akhirnya, pikirkan lambda sebagai berperilaku sebagai metode anomi dan Proc.new baru berperilaku sebagai kode inline.

Donato
sumber
0

Pos bermanfaat tentang panduan ruby: blok, procs & lambdas

Procs kembali dari metode saat ini, sementara lambdas kembali dari lambda itu sendiri.

Procs tidak peduli dengan jumlah argumen yang benar, sementara lambdas akan memunculkan pengecualian.

Sindhu Shree
sumber
-3

perbedaan antara proc dan lambda adalah proc hanya salinan kode dengan argumen diganti pada gilirannya, sedangkan lambda adalah fungsi seperti dalam bahasa lain. (perilaku pengembalian, pemeriksaan argumen)

Nighon
sumber