Когда использовать лямбда, когда использовать Proc.new?

голоса
316

В Ruby 1.8, есть тонкие различия между Proc / лямбда с одной стороны, и Proc.newс другой стороны .

  • Каковы эти различия?
  • Можете ли вы дать рекомендации о том, чтобы решить, какой из них выбрать?
  • В Ruby 1.9, прок и лямбда различны. В чем дело?
Задан 03/08/2008 в 07:40
источник пользователем
На других языках...                            


14 ответов

голоса
364

Другое важное , но тонкое различие между проками , созданных с lambdaи проками созданы Proc.newкак они обращаются с returnзаявлением:

  • В lambda-created прока, то returnоператор возвращает только из самого прока
  • В Proc.new-created прока, то returnутверждение немного более удивительно: она возвращает управление не только из прока, но и от метода ограждающего процедурным!

Вот lambda-created Proc - й returnв действии. Он ведет себя так , что вы , вероятно , ожидать:

def whowouldwin

  mylambda = lambda {return "Freddy"}
  mylambda.call

  # mylambda gets called and returns "Freddy", and execution
  # continues on the next line

  return "Jason"

end


whowouldwin
#=> "Jason"

Теперь вот Proc.new-created прок - х returnделает то же самое. Вы собираетесь увидеть один из тех случаев , когда Руби ломает восхваляемый Принцип наименьшего удивления:

def whowouldwin2

  myproc = Proc.new {return "Freddy"}
  myproc.call

  # myproc gets called and returns "Freddy", 
  # but also returns control from whowhouldwin2!
  # The line below *never* gets executed.

  return "Jason"

end


whowouldwin2         
#=> "Freddy"

Благодаря этому удивительному поведению (а также менее типизации), я , как правило , в пользу использования lambdaболее Proc.newпри принятии проков.

Ответил 03/08/2008 d 16:21
источник пользователем

голоса
93

Для того, чтобы предоставить дополнительные разъяснения:

Джо говорит , что поведение возвращения Proc.newудивительно. Однако если учесть , что Proc.new ведет себя как блок это не удивительно , так как это именно то, как блоки ведут себя. lambas с другой стороны ведут себя скорее как методы.

На самом деле это объясняет, почему Procs является гибким, когда речь идет о арности (число аргументов), тогда как лямбда нет. Блоки не требуют, чтобы все их аргументы, которые будут предоставлены, но методы делать (если не предусмотрено по умолчанию). Обеспечивая лямбда-аргумента по умолчанию это не вариант в Ruby 1.8, теперь поддерживается в Ruby 1.9 с альтернативным синтаксисом лямбда (как отмечено webmat):

concat = ->(a, b=2){ "#{a}#{b}" }
concat.call(4,5) # => "45"
concat.call(1)   # => "12"

И Михель де Маре (ОП) неверен о Procs и лямбда ведут себя то же самое с арностью в Ruby 1.9. Я проверил, что они до сих пор поддерживают поведение от 1,8, как указано выше.

breakзаявления на самом деле не имеет смысла в любом Procs или лямбды. В Procs, перерыв будет возвращать вас из Proc.new, которая уже завершена. И это не имеет никакого смысла, чтобы вырваться из лямбда, поскольку это по существу метод, и вы никогда не ломаются от верхнего уровня метода.

next, redoИ raiseведут себя одинаково в обоих Procs и лямбды. В то время retryне допускается ни в одном и возбудит исключение.

И , наконец, procметод никогда не должен использоваться , поскольку он не соответствует и имеет неожиданное поведение. В Ruby 1.8 фактически возвращает лямбда! В Ruby 1.9 это было исправлено , и она возвращает Proc. Если вы хотите создать Proc, придерживайтесь Proc.new.

Для получения дополнительной информации, я настоятельно рекомендую издательство O'Reilly The Ruby Programming Language , который является моим источником для большей части этой информации.

Ответил 04/10/2009 d 06:23
источник пользователем

голоса
41

Я нашел эту страницу , которая показывает , что разница между Proc.newи lambdaесть. Согласно странице, единственное отличие состоит в том , что лямбда - строга о количестве аргументов он принимает, а Proc.newобращенные отсутствуют аргументы nil. Ниже приведен пример , иллюстрирующий сеанс ЭСО разницу:

IRB (основной): 001: 0> л = лямбда {| х, у | х + у}
=> # <Proc: 0x00007fc605ec0748 @ (IRB): 1>
IRB (основной): 002: 0> р = Proc.new {| х, у | х + у}
=> # <Proc: 0x00007fc605ea8698 @ (IRB): 2>
IRB (основной): 003: 0> l.call "привет", "мир"
=> "HelloWorld"
IRB (основной): 004: 0> p.call "привет", "мир"
=> "HelloWorld"
IRB (основной): 005: 0> l.call "привет"
ArgumentError: неверное число аргументов (1 для 2)
    из (IRB): 1
    из (IRB): 5: в `вызов»
    из (IRB): 5
    от: 0
IRB (основной): 006: 0> p.call "привет"
TypeError: не может преобразовать ноль в строку
    из (IRB): 2: в '+»
    из (IRB): 2
    из (IRB): 6: в `вызов»
    из (IRB): 6
    от: 0

На этой странице также рекомендует использовать лямбда, если вы специально не хотите толерантное поведение ошибки. Я согласен с этим чувством. Использование лямбда кажется чуть более кратким, и с такой незначительной разницей, что, кажется, лучший выбор в среднем положении.

Что касается Ruby 1.9, извините, я не смотрел в 1.9, но я не думаю, что они бы изменить все, что многое (не верьте мне на слово, хотя, кажется, вы уже слышали о некоторых изменениях, так Я, наверное, неправильно там).

Ответил 03/08/2008 d 08:28
источник пользователем

голоса
14

Proc старше, но семантика возвращения весьма нелогичная для меня (по крайней мере, когда я изучал язык), потому что:

  1. Если вы используете процедурный, вы, скорее всего, с помощью какой-то функциональной парадигмы.
  2. Proc может вернуться из области видимости (см предыдущих ответов), которая представляет собой Гота в основном, и в высшей степени нефункциональный в природе.

Лямбда функционально безопаснее и легче рассуждать о - я всегда использую его вместо прока.

Ответил 11/09/2008 d 00:32
источник пользователем

голоса
11

Я не могу сказать много о тонких различиях. Тем не менее, я могу отметить, что Ruby 1.9 теперь позволяет дополнительные параметры для лямбды и блоков.

Вот новый синтаксис для Stabby лямбды под 1.9:

stabby = ->(msg='inside the stabby lambda') { puts msg }

Рубин 1.8 не было такой синтаксис. Ни сделал обычный способ объявления блоков / лямбды не поддерживают дополнительные аргументы:

# under 1.8
l = lambda { |msg = 'inside the stabby lambda'|  puts msg }
SyntaxError: compile error
(irb):1: syntax error, unexpected '=', expecting tCOLON2 or '[' or '.'
l = lambda { |msg = 'inside the stabby lambda'|  puts msg }

Ruby 1.9, однако, поддерживает необязательные аргументы, даже со старым синтаксисом:

l = lambda { |msg = 'inside the regular lambda'|  puts msg }
#=> #<Proc:0x0e5dbc@(irb):1 (lambda)>
l.call
#=> inside the regular lambda
l.call('jeez')
#=> jeez

Если вы хотите построить ruby1.9 для Leopard или Linux, проверить эту статью (бесстыдные саморекламы).

Ответил 19/11/2008 d 22:28
источник пользователем

голоса
10

Короткий ответ: Важно то , что returnделает: лямбда возвращается из себя, и процедура возвращает из себя И функции , которая вызвала его.

Что менее ясно, почему вы хотите использовать каждый. лямбда-это то, что мы ожидаем, что вещи должны делать в функциональном смысле программирования. Это в основном анонимный метод с текущей областью автоматически связаны. Из этих двух лямбда является тот, который вы, вероятно, следует использовать.

Proc, с другой стороны, это действительно полезно для реализации самого языка. Например, вы можете реализовать «если» заявление или «для» петли с ними. Любое возвращение найдено в прок будет возвращать из метода, вызвавшего его, а не только «если» заявление. Это как работают языки, как «если» заявления работать, так что моя догадка Рубин использует это под одеялом, и они просто выставили ее, потому что это казалось мощным.

Вы только действительно должны были бы это, если вы создаете новые языковые конструкции, как петли, если-ELSE конструкций и т.д.

Ответил 06/10/2011 d 19:33
источник пользователем

голоса
9

Хороший способ, чтобы увидеть его в том, что лямбды выполняются в их собственной области (как если бы это был вызов метода), в то время как Procs можно рассматривать как выполняется рядный с помощью метода вызова, по крайней мере, это хороший способ решить которым один использовать в каждом случае.

Ответил 09/12/2008 d 15:17
источник пользователем

голоса
8

Я не заметил каких-либо комментариев по третьему методу в Queston, «Proc», который является устаревшим, но обрабатываются по-разному в 1,8 и 1,9.

Вот довольно подробный пример того, что делает его легко увидеть различие между этими тремя аналогичными вызовами:

def meth1
  puts "method start"

  pr = lambda { return }
  pr.call

  puts "method end"  
end

def meth2
  puts "method start"

  pr = Proc.new { return }
  pr.call

  puts "method end"  
end

def meth3
  puts "method start"

  pr = proc { return }
  pr.call

  puts "method end"  
end

puts "Using lambda"
meth1
puts "--------"
puts "using Proc.new"
meth2
puts "--------"
puts "using proc"
meth3
Ответил 25/06/2009 d 12:22
источник пользователем

голоса
7

Затворы в Ruby , хороший обзор для того, как блоки, лямбда и прок работают в Ruby, с Ruby.

Ответил 28/08/2008 d 14:50
источник пользователем

голоса
6

Понимание Ruby , блоки, проки и лямбды Роберт Sosinski четко объясняет эти концепции программирования и подкрепляет объяснения с примерами кода. Объекты Метод связаны между собой и покрыты , а также.

Ответил 23/08/2013 d 18:07
источник пользователем

голоса
5

лямбда работает как и ожидалось, как и в других языках.

проводная Proc.new является удивительным и запутанным.

returnЗаявление в прок , созданный Proc.newне только возвращает управление только от себя, но и от метода заключая его .

def some_method
  myproc = Proc.new {return "End."}
  myproc.call

  # Any code below will not get executed!
  # ...
end

Можно утверждать , что Proc.newвставляет код в метод ограждающей, так же , как блок. Но Proc.newсоздает объект, в то время как блок является частью объекта.

И есть еще одно различие между лямбда и Proc.new, что их обработка (неправильных) аргументов. Лямбда жалуется на него, в то время как Proc.newигнорирует дополнительные аргументы или считает отсутствие аргументов как ноль.

irb(main):021:0> l = -> (x) { x.to_s }
=> #<Proc:0x8b63750@(irb):21 (lambda)>
irb(main):022:0> p = Proc.new { |x| x.to_s}
=> #<Proc:0x8b59494@(irb):22>
irb(main):025:0> l.call
ArgumentError: wrong number of arguments (0 for 1)
        from (irb):21:in `block in irb_binding'
        from (irb):25:in `call'
        from (irb):25
        from /usr/bin/irb:11:in `<main>'
irb(main):026:0> p.call
=> ""
irb(main):049:0> l.call 1, 2
ArgumentError: wrong number of arguments (2 for 1)
        from (irb):47:in `block in irb_binding'
        from (irb):49:in `call'
        from (irb):49
        from /usr/bin/irb:11:in `<main>'
irb(main):050:0> p.call 1, 2
=> "1"

Кстати, procв Ruby 1.8 создает лямбда, в то время как рубин 1.9+ ведет себя как Proc.new, который действительно сбивает с толку.

Ответил 29/10/2014 d 16:22
источник пользователем

голоса
3

Чтобы уточнить ответ аккордеона Гая:

Обратите внимание , что Proc.newсоздает процедурный путем передается блок. Я считаю , что lambda {...}анализируется как своего рода буквальным, а не вызова метода , который проходит блок. returnИНГ внутри блока , подключенного к вызову метода будет возвращать из метода, а не блок, а Proc.newслучай является примером этого в игре.

(Это 1,8. Я не знаю, как это приводит к 1.9.)

Ответил 07/09/2008 d 03:31
источник пользователем

голоса
2

Я немного опоздал на это, но есть один большой , но мало известно о том, что Proc.newне упоминается в комментариях вообще. Как по документации :

Proc::newможет быть вызван без блока только в пределах метода с прикрепленным блоком, в этом случае , что блок преобразуется вProc объект.

Тем не менее, Proc.newпозволяет методам цепи с получением:

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!
Ответил 28/04/2015 d 13:15
источник пользователем

голоса
1

Разница в поведении с returnИМХО самое важное различие между 2. Я также предпочитаю лямбда , потому что это меньше , чем типирование Proc.new :-)

Ответил 11/08/2008 d 03:09
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more