Dan kapan Anda akan menggunakan yang satu daripada yang lain?
Dan kapan Anda akan menggunakan yang satu daripada yang lain?
Jawaban:
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.
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
Hanya ada dua perbedaan utama.
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.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"
# 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
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 }
a = proc { |x| x 2 }sama dengana = Proc.new { |x| x 2 }
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.
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.
returnpernyataan kembaliprocdibandingkan denganlambda.