А коли б ти скористався одним, а не іншим?
А коли б ти скористався одним, а не іншим?
Відповіді:
Одна відмінність полягає в тому, як вони обробляють аргументи. Створення закупівлі за допомогою proc {}
та Proc.new {}
еквівалент. Однак, використовуючи, lambda {}
ви отримуєте Proc, який перевіряє кількість аргументів, переданих йому. Від ri Kernel#lambda
:
Еквівалентно Proc.new , за винятком об'єктів Proc, що перевіряють кількість параметрів, переданих при виклику .
Приклад:
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)
Крім того, як вказує Кен, використовуючи return
всередині лямбда повертає значення цієї лямбда, але використовуючи return
в програмі, повертається з блоку, що додається.
lambda { return :foo }.call # => :foo
return # => LocalJumpError: unexpected return
Proc.new { return :foo }.call # => LocalJumpError: unexpected return
Отже, для більшості швидких застосувань вони однакові, але якщо ви хочете автоматичну строгу перевірку аргументів (що також іноді може допомогти при налагодженні), або якщо вам потрібно використовувати return
оператор для повернення значення proc, використовуйте lambda
.
Справжня різниця між програмами та лямбдами пов'язана з ключовими словами управління потоком. Я говорю про те return
, raise
, break
, redo
, і retry
т.д. - тих керуючих словах. Скажімо, у вас є заява про повернення в проц. Коли ви зателефонуєте на ваш прок, він не тільки скине вас з нього, але й повернеться із способу, що додає, наприклад:
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
Фінал puts
у методі ніколи не був виконаний, оскільки коли ми зателефонували нашому процесору, return
всередині нього викинуло нас із методу. Якщо, однак, ми перетворимо наш Pro в лямбда, ми отримаємо наступне:
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
перевіряє кількість переданих йому аргументів, а a proc
- ні. Це означає, що алієтика lambda
видасть помилку, якщо ви передасте їй неправильну кількість аргументів, тоді як proc
архів буде ігнорувати несподівані аргументи та призначати nil
будь-які відсутні.lambda
повертається, він передає управління назад методу виклику; коли proc
повертається, це робить це негайно, не повертаючись до методу виклику.Щоб побачити, як це працює, подивіться на код нижче. Наш перший метод називає a proc
; другий дзвінки а 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!"
Подивіться, як proc
говорить "Бетмен переможе!", Це тому, що він повертається негайно, не повертаючись до методу batman_ironman_proc.
Наша lambda
, однак, повертається в метод після виклику, тому метод повертає код останнього він оцінює: «Залізна людина переможе»
# Приклади програми
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
# Приклади лямбда
lam = lambda { |x| puts x*2 }
[1,2,3].each(&lam)
lam = lambda { puts "Hello World" }
lam.call
Відмінності між Проксом і Лямбда
Перш ніж я потрапляю на відмінності між документами та лямбдами, важливо згадати, що вони обидва об'єкти Proc.
proc = Proc.new { puts "Hello world" }
lam = lambda { puts "Hello World" }
proc.class # returns 'Proc'
lam.class # returns 'Proc'
Однак лямбда - це інший "аромат" програм. Ця незначна різниця проявляється при поверненні предметів.
proc # returns '#<Proc:0x007f96b1032d30@(irb):75>'
lam # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'
1. Лямбда перевіряє кількість аргументів, тоді як протоколи - ні
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)
На противагу цьому, програмі не важливо, чи їм передано неправильну кількість аргументів.
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. Лямбди та програми трактують ключове слово "повернення" по-різному
'return' всередині лямбда запускає код прямо поза кодом лямбда
def lambda_test
lam = lambda { return }
lam.call
puts "Hello world"
end
lambda_test # calling lambda_test prints 'Hello World'
'return' всередині proc запускає код поза методом, у якому виконуються proc
def proc_test
proc = Proc.new { return }
proc.call
puts "Hello world"
end
proc_test # calling proc_test prints nothing
І щоб відповісти на ваш інший запит, який з них використовувати і коли? Я піду за @jtbandes, як він уже згадував
Отже, для більшості швидких застосувань вони однакові, але якщо ви хочете автоматичну строгу перевірку аргументів (що також іноді може допомогти при налагодженні), або якщо вам потрібно використовувати оператор return, щоб повернути значення proc, використовуйте лямбда.
Спочатку розміщено тут
Взагалі кажучи, лямбди більш інтуїтивні, ніж програми, тому що вони більш схожі на методи. Вони досить суворо ставляться до сваволі, і вони просто виходять, коли ви дзвоните. З цієї причини багато рубіністів використовують лямбда в якості першого вибору, якщо їм не потрібні специфічні особливості програм.
Документи: Об’єкти класу Proc
. Як і блоки, вони оцінюються в області, де вони визначені.
Лямбда: також об'єкти класу, Proc
але тонко відрізняються від звичайних проектів. Вони закриваються як блоки та документи, і як такі вони оцінюються в області, де вони визначені.
Створення Proc
a = Proc.new { |x| x 2 }
Створення лямбда
b = lambda { |x| x 2
}
a = proc { |x| x 2 }
те саме, щоa = Proc.new { |x| x 2 }
Ось ще один спосіб зрозуміти це.
Блок - це фрагмент коду, приєднаний до виклику до виклику методу на об'єкті. У наведеному нижче прикладі self - це екземпляр анонімного класу, успадкованого від ActionView :: Base в рамках Rails (який сам включає багато допоміжних модулів). картка - це метод, який ми закликаємо до себе. Ми передаємо аргумент методу, а потім завжди прикріплюємо блок до кінця виклику методу:
self.card :contacts do |c|
// a chunk of valid ruby code
end
Гаразд, тому ми передаємо шматок коду методу. Але як ми можемо використовувати цей блок? Один із варіантів - перетворити фрагмент коду в об'єкт. Рубі пропонує три способи перетворення фрагмента коду в об'єкт
# 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
У вищеописаному методі & перетворює переданий блок методу в об'єкт і зберігає цей об'єкт у локальному блоці змінної. Насправді ми можемо показати, що вона має таку саму поведінку, як лямбда та Proc.new:
def add(&block)
block
end
l3 = add { |a| a + 1 }
l3.call(1)
=> 2
Це ВАЖЛИВО. Коли ви передаєте блок методу та перетворюєте його за допомогою &, об'єкт, який він створює, використовує Proc.new для перетворення.
Зауважте, що я уникав використання "proc" як опції. Це тому, що це Ruby 1.8, це те саме, що і лямбда, а у Ruby 1.9 - це те саме, що і Proc.new, і у всіх версіях Ruby цього слід уникати.
Тоді ви запитаєте, в чому різниця між лямбда і Proc.new?
По-перше, що стосується передачі параметрів, лямбда поводиться як виклик методу. Це призведе до виключення, якщо ви передасте неправильну кількість аргументів. Навпаки, Proc.new поводиться як паралельне призначення. Усі невикористані аргументи перетворюються на нуль:
> 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 +
По-друге, лямбда та Proc.new по-різному обробляють ключове слово повернення. Коли ви робите повернення всередину Proc.new, воно фактично повертається із методу, що додає, тобто оточуючого контексту. Коли ви повертаєтесь з лямбда-блоку, він просто повертається з блоку, а не методу укладання. В основному, він виходить з виклику до блоку і продовжує виконання з рештою способу, що додає.
> 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"
То чому ця поведінкова різниця? Причина полягає в тому, що за допомогою Proc.new ми можемо використовувати ітератори всередині контексту методів, що складаються укладаючи, та робити логічні висновки. Подивіться на цей приклад:
> 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
Ми очікуємо, що коли ми будемо викликати повернення всередині ітератора, він повернеться із методу, що додає. Пам'ятайте, що блоки, передані ітераторам, перетворюються на об'єкти за допомогою Proc.new, і саме тому, коли ми будемо використовувати return, він вийде з методу укладання.
Ви можете вважати лямбдаси як анонімні методи, вони виділяють окремі блоки коду в об’єкт, який можна розглядати як метод. Зрештою, подумайте про лямбду як про аномічний метод, а Proc.new поводиться як вбудований код.
Корисна публікація про рубінові путівники: блоки, програми та лямбда
Програми повертаються з поточного методу, в той час як лямбда повертаються з самої лямбда.
Програми не переймаються правильною кількістю аргументів, в той час як лямбди будуть винятком.
return
повертається твердженняproc
протиlambda
.