И когда вы будете использовать один, а не другой?
Какая разница между проком и лямбдой в Ruby?
Ответ 1
Одно отличие в том, как они обрабатывают аргументы. Создание proc с использованием proc {}
и Proc.new {}
эквивалентно. Однако использование lambda {}
дает вам proc, который проверяет количество переданных ему аргументов. Из ri Kernel#lambda
:
Эквивалент Proc.new, за исключением того, что в результате объекты Proc проверяют количество параметров, переданных при вызове.
Пример:
p = Proc.new {|a, b| puts a**2+b**2 } # => #<Proc:[email protected](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:[email protected](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
в proc возвращается из закрывающего блока.
lambda { return :foo }.call # => :foo
return # => LocalJumpError: unexpected return
Proc.new { return :foo }.call # => LocalJumpError: unexpected return
Таким образом, для наиболее быстрого использования они одинаковы, но если вы хотите автоматическую проверку строгих аргументов (что также может иногда помочь при отладке), или если вам нужно использовать оператор return
для возврата значения proc, используйте lambda
.
Ответ 2
Реальная разница между procs и lambdas имеет все, что связано с ключевыми словами потока управления. Я говорю о return
, raise
, break
, redo
, retry
и т.д. - эти управляющие слова. Скажем, у вас есть оператор return в proc. Когда вы вызываете свой proc, он не только выгрузит вас из него, но также вернется из прилагаемого метода, например:
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
[email protected]:~/tmp$ ruby a.rb
before proc
inside proc
Окончательный puts
в методе никогда не выполнялся, поскольку, когда мы вызывали наш proc, return
внутри него выкинул нас из метода. Если, однако, мы преобразуем наш proc в лямбда, мы получим следующее:
def my_method
puts "before proc"
my_proc = lambda do
puts "inside proc"
return
end
my_proc.call
puts "after proc"
end
my_method
[email protected]:~/tmp$ ruby a.rb
before proc
inside proc
after proc
Возвращение внутри лямбда только выгружает нас из самой лямбда, и метод размещения продолжает выполняться. Как ключевые слова потока управления обрабатываются в procs, а lambdas - основное различие между ними.
Ответ 3
В дополнение к ответу jtbandes также существует разница в том, что возвращает оператор return
из proc
по сравнению с lambda
.
Ответ 4
Это несколько тонкий. Это оба метода, которые создают замыкания, и оба возвращают объекты Proc. На самом деле есть и третий способ - Proc.new
. Разница заключается в том, как они себя ведут, и зависит от того, используете ли вы Ruby 1.8 или 1.9 (фактически, есть еще один способ создать их в Ruby 1.9). В общем случае разница - это не то, о чем вам нужно беспокоиться. Только когда вы беспокоитесь о строгости, это имеет значение. Когда использовать лямбда, когда использовать Proc.new? достаточно хорошо описывает различия.
Ответ 5
Есть только два основных отличия.
- Во-первых,
lambda
проверяет количество аргументов, переданных ей, аproc
- нет. Это означает, чтоlambda
выдаст ошибку, если вы передаёте неверное число аргументов, в то время какproc
будет игнорировать неожиданные аргументы и присвоитьnil
любого, что не хватает. - Во-вторых, когда
lambda
возвращается, она передает управление обратно вызывающему методу; когдаproc
возвращается, он делает это немедленно, не возвращаясь к вызывающему методу.
Чтобы увидеть, как это работает, взгляните на код ниже. Наш первый метод вызывает 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
возвращается в метод после вызова, поэтому метод возвращает последний код, который он оценивает: "Железный человек победит!"
Ответ 6
Вообще говоря, лямбды более интуитивны, чем procs, потому что theyre больше похоже на методы. Theyre довольно строгие о arity, и они просто выход, когда вы вызываете return. По этой причине многие рубисты используют лямбда как первый выбор, если им не нужны специфические особенности procs.
Procs: Объекты класса Proc
. Подобно блокам, они оцениваются в области
где они определены.
Lambdas: Также объекты класса Proc
, но тонко отличаются от обычных procs.
Theyre замыкает, как блоки и procs, и, как таковые, они оцениваются в
область, в которой они определены.
Создание Proc
a = Proc.new { |x| x 2 }
Создание лямбда
b = lambda { |x| x 2
}
Ответ 7
Вот еще один способ понять это.
Блок - это фрагмент кода, прикрепленный к вызову метода объекта. В приведенном ниже примере self является экземпляром анонимного класса, унаследованного от ActionView :: Base в платформе Rails (который сам включает в себя множество вспомогательных модулей). карта - это метод, который мы называем собой. Мы передаем аргумент методу, а затем всегда присоединяем блок к концу вызова метода:
self.card :contacts do |c|
// a chunk of valid ruby code
end
Итак, мы передаем кусок кода методу. Но как мы используем этот блок? Один из вариантов - преобразовать кусок кода в объект. Ruby предлагает три способа конвертировать кусок кода в объект
# 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:[email protected](irb):19 (lambda)>
> l.call(1)
ArgumentError: wrong number of arguments (1 for 2)
> l2 = Proc.new {|a,b| puts "#{a} + #{b}" }
=> #<Proc:[email protected](irb):21>
> l2.call(1)
1 +
Во-вторых, lambda и Proc.new обрабатывают ключевое слово return по-разному. Когда вы делаете возврат внутри 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
Мы ожидаем, что когда мы вызовем return внутри итератора, он вернется из включающего метода. Помните, что блоки, переданные итераторам, преобразуются в объекты с использованием Proc.new, и поэтому, когда мы используем return, он выходит из метода включения.
Вы можете думать о лямбдах как об анонимных методах, они изолируют отдельные блоки кода в объект, который можно рассматривать как метод. В конечном счете, думайте, что лямбда ведет себя как аномный метод, а Proc.new ведет себя как встроенный код.
Ответ 8
Полезный пост на рубиновых путеводителях: блоки, процы и лямбды
Процессы возвращаются из текущего метода, а лямбды возвращаются из самой лямбды.
Procs не заботится о правильном количестве аргументов, в то время как лямбды вызовут исключение.
Ответ 9
# 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
# Лямбда Примеры
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:[email protected](irb):75>'
lam # returns '<Proc:[email protected](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. Lambdas и procs по-разному обрабатывают ключевое слово 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, используйте lambda.
Первоначально опубликовано здесь
Ответ 10
различия между proc и лямбдой заключаются в том, что proc - это просто копия кода с замененными аргументами, в то время как лямбда - это функция, как на других языках. (поведение возврата, проверки аргументов)