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

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

  • В чем эти отличия?
  • Можете ли вы дать рекомендации, как решить, какой из них выбрать?
  • В Ruby 1.9 proc и lambda отличаются. В чем дело?

344
2

  • Также см. Подробный пост [Различия в потоке управления между Ruby Procs и Lambdas] ( ...
14
Общее количество ответов: 14

Ответ #1

Ответ на вопрос: Когда использовать лямбду, когда использовать Proc.new?

Еще одно важное, но тонкое различие между 9X_lambda-functions процессами, созданными с помощью lambda, и процессами, созданными 9X_lambda-expressions с помощью Proc.new, заключается в том, как они обрабатывают 9X_generic-lambda оператор return:

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

Вот созданный lambda процесс return в действии. Он 9X_ruby ведет себя так, как вы, вероятно, ожидаете:

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" 

Теперь 9X_lambda то же самое делает Proc.new созданный процесс return. Вы 9X_lambda-functions скоро увидите один из тех случаев, когда 9X_lambda-functions Ruby нарушает хваленый принцип наименьшего 9X_proc удивления:

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" 

Благодаря этому удивительному 9X_lambda поведению (а также тому, что я меньше печатаю), я 9X_lambda-expressions предпочитаю использовать lambda, а не Proc.new при создании 9X_lambda проков.

383
7

  • @panzi, да, [`proc` эквивалентно` Proc.new`] (http://www.ruby-doc.org/core/classes/K ...

Ответ #2

Ответ на вопрос: Когда использовать лямбду, когда использовать Proc.new?

Чтобы дать дополнительные пояснения:

Джои 9X_lambdaexpression говорит, что поведение Proc.new при возврате вызывает 9X_lambda-expressions удивление. Однако, если учесть, что Proc.new 9X_lambda ведет себя как блок, это неудивительно, поскольку 9X_proc именно так ведут себя блоки. с другой стороны, ламбы 9X_lambdaexpression больше похожи на методы.

Это фактически объясняет, почему 9X_lambda-functions Procs гибки, когда дело касается арности 9X_lambda-functions (количества аргументов), а лямбды - нет. Блоки 9X_proc не требуют предоставления всех своих аргументов, но 9X_lambda методы требуют (если не указано значение 9X_ruby по умолчанию). Хотя предоставление лямбда-аргумента 9X_proc по умолчанию не является опцией в Ruby 1.8, теперь 9X_lambda-expressions оно поддерживается в Ruby 1.9 с альтернативным 9X_lambdaexpression синтаксисом лямбда-выражения (как отмечает 9X_lambda-functions webmat):

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

И Мишель де Маре (OP) неверно говорит 9X_lambda-functions о том, что Procs и лямбда ведут себя одинаково 9X_lambdaexpression с arity в Ruby 1.9. Я убедился, что они 9X_lambda-functions по-прежнему поддерживают поведение начиная 9X_proc с версии 1.8, как указано выше.

Операторы

break на самом 9X_lambda-functions деле не имеют особого смысла ни в Procs, ни 9X_lambda-functions в лямбдах. В Procs перерыв вернет вас из 9X_generic-lambda Proc.new, который уже был завершен. И нет 9X_lambdaexpression никакого смысла отказываться от лямбды, поскольку 9X_proc это, по сути, метод, и вы никогда не откажетесь 9X_ruby от верхнего уровня метода.

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

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

Для получения дополнительной 9X_generic-lambda информации я настоятельно рекомендую O'Reilly 9X_proc Язык программирования Ruby, который является моим источником большей 9X_lambda-expressions части этой информации.

95
2

  • Начиная с Ruby 2.5, `break` из Procs выз ...

Ответ #3

Ответ на вопрос: Когда использовать лямбду, когда использовать Proc.new?

Я нашел this page, что показывает разницу между Proc.new и 9X_lambda-functions lambda. Согласно странице, единственное отличие 9X_lambda состоит в том, что лямбда строга в отношении 9X_generic-lambda количества принимаемых аргументов, тогда 9X_generic-lambda как Proc.new преобразует отсутствующие аргументы 9X_generic-lambda в nil. Вот пример сеанса IRB, иллюстрирующий 9X_lambdaexpression разницу:

irb(main):001:0> l = lambda { |x, y| x + y }
=> #
irb(main):002:0> p = Proc.new { |x, y| x + y }
=> #
irb(main):003:0> l.call "hello", "world"
=> "helloworld"
irb(main):004:0> p.call "hello", "world"
=> "helloworld"
irb(main):005:0> l.call "hello"
ArgumentError: wrong number of arguments (1 for 2)
    from (irb):1
    from (irb):5:in `call'
    from (irb):5
    from :0
irb(main):006:0> p.call "hello"
TypeError: can't convert nil into String
    from (irb):2:in `+'
    from (irb):2
    from (irb):6:in `call'
    from (irb):6
    from :0

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

Что касается 9X_generic-lambda Ruby 1.9, извините, я еще не смотрел в 1.9, но 9X_lambda я не думаю, что они будут настолько сильно 9X_lambda меняться (хотя не верьте мне на слово, кажется, вы 9X_proc слышали о некоторых изменениях, так что 9X_lambda тут я наверное не прав)

42
1

  • procs также возвращ ...

Ответ #4

Ответ на вопрос: Когда использовать лямбду, когда использовать Proc.new?

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

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

Lambda функционально безопаснее 9X_lambda-expressions и проще — я всегда использую ее вместо proc.

15
0

Ответ #5

Ответ на вопрос: Когда использовать лямбду, когда использовать Proc.new?

Я не могу много сказать о тонких различиях. Тем 9X_lambdaexpression не менее, я могу отметить, что Ruby 1.9 9X_generic-lambda теперь допускает необязательные параметры 9X_lambda-expressions для лямбда-выражений и блоков.

Вот новый 9X_ruby синтаксис для stabby lambdas в версии 1.9:

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

В 9X_proc Ruby 1.8 такого синтаксиса не было. Также 9X_lambda-expressions традиционный способ объявления блоков / лямбда-выражений 9X_lambda-expressions не поддерживает необязательные аргументы:

# 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 9X_lambda 1.9, однако, поддерживает необязательные 9X_proc аргументы даже со старым синтаксисом:

l = lambda { |msg = 'inside the regular lambda'| puts msg } #=> # l.call #=> inside the regular lambda l.call('jeez') #=> jeez 

Если 9X_ruby вы хотите собрать Ruby1.9 для Leopard или 9X_lambdaexpression Linux, попробуйте this article (бессовестная самореклама).

11
0

Ответ #6

Ответ на вопрос: Когда использовать лямбду, когда использовать Proc.new?

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

11
0

Ответ #7

Ответ на вопрос: Когда использовать лямбду, когда использовать Proc.new?

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

Менее 9X_proc ясно, почему вы хотите использовать каждый 9X_generic-lambda из них. лямбда - это то, что мы ожидаем 9X_lambdaexpression от вещей в смысле функционального программирования. По 9X_lambda сути, это анонимный метод с автоматически 9X_lambdaexpression привязанной текущей областью видимости. Из 9X_lambda этих двух вам, вероятно, следует использовать 9X_lambda-expressions лямбда.

Proc, с другой стороны, действительно 9X_proc полезен для реализации самого языка. Например, с 9X_ruby ними можно реализовать операторы if или 9X_generic-lambda циклы for. Любой возврат, найденный в процедуре, будет 9X_ruby возвращен из метода, который его вызвал, а 9X_ruby не только из оператора «if». Вот как работают 9X_generic-lambda языки, как работают операторы «если», поэтому 9X_lambdaexpression я предполагаю, что Ruby использует это под 9X_lambdaexpression прикрытием, и они просто раскрыли его, потому 9X_lambda что это казалось мощным.

Это действительно 9X_lambda может понадобиться вам только в том случае, если 9X_generic-lambda вы создаете новые языковые конструкции, такие 9X_proc как циклы, конструкции if-else и т. д.

11
1

  • лямбда возвращается из себя, а процедура возвращается из себя. И функция, которая ее вызвала, "является явной оши ...

Ответ #8

Ответ на вопрос: Когда использовать лямбду, когда использовать Proc.new?

Я не заметил никаких комментариев к третьему 9X_ruby методу в квестоне, "proc", который устарел, но 9X_lambda обрабатывается по-другому в 1.8 и 1.9.

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

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 

7
1

  • Мац заявил, что планировал отказаться от него, потому что было запутано, если proc и Proc.new возвращали разные результаты. Однако в 1.9 они ведут себя так же (proc - это псевдоним для Proc.new). http://eigenclass.or ...

Ответ #9

Ответ на вопрос: Когда использовать лямбду, когда использовать Proc.new?

Closures in Ruby — хороший обзор того, как блоки, лямбда-выражения 9X_generic-lambda и процедуры работают в Ruby с помощью Ruby.

6
0

Ответ #10

Ответ на вопрос: Когда использовать лямбду, когда использовать Proc.new?

лямбда работает должным образом, как и на 9X_proc других языках.

Проводной Proc.new удивляет и сбивает 9X_lambda-functions с толку.

Оператор return в процедуре, созданной 9X_lambdaexpression Proc.new, вернет управление не только от себя, но 9X_generic-lambda и также от метода, включающего его.

def some_method myproc = Proc.new {return "End."} myproc.call # Any code below will not get executed! # ... end 

Вы можете утверждать, что Proc.new вставляет 9X_ruby код во включающий метод, как и блок. Но 9X_proc Proc.new создает объект, а блок является частью объекта.

Есть 9X_lambda-functions еще одно различие между лямбдой и Proc.new, которое 9X_ruby заключается в обработке (неправильных) аргументов. lambda 9X_proc жалуется на это, в то время как Proc.new игнорирует 9X_ruby лишние аргументы или считает отсутствие 9X_lambda аргументов нулевым.

irb(main):021:0> l = -> (x) { x.to_s } => # irb(main):022:0> p = Proc.new { |x| x.to_s} => # 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 `
' 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 `
' irb(main):050:0> p.call 1, 2 => "1"

Кстати, proc в Ruby 1.8 создает 9X_lambdaexpression лямбду, а в Ruby 1.9+ ведет себя как Proc.new, что 9X_lambda-functions действительно сбивает с толку.


              
5
0

Ответ #11

Ответ на вопрос: Когда использовать лямбду, когда использовать Proc.new?

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

Обратите 9X_lambda-functions внимание, что Proc.new создает прок, передавая блок. Я 9X_generic-lambda считаю, что lambda {...} анализируется как своего рода 9X_ruby литерал, а не вызов метода, который передает 9X_lambdaexpression блок. returnвыполнение изнутри блока, прикрепленного 9X_lambda-functions к вызову метода, будет возвращаться из метода, а 9X_lambda-expressions не из блока, и случай Proc.new является примером 9X_lambda-expressions этого в игре.

(Это 1.8. Я не знаю, как это 9X_ruby переводится в 1.9.)

3
0

Ответ #12

Ответ на вопрос: Когда использовать лямбду, когда использовать Proc.new?

Я немного опоздал с этим, но есть одна замечательная, но 9X_lambda малоизвестная вещь о Proc.new, которая вообще не 9X_generic-lambda упоминается в комментариях. Согласно documentation:

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

Тем не менее, Proc.new позволяет связывать 9X_ruby методы получения результатов:

def m1 yield 'Finally!' if block_given? end def m2 m1 &Proc.new end m2 { |e| puts e } #⇒ Finally! 

3
0

Ответ #13

Ответ на вопрос: Когда использовать лямбду, когда использовать Proc.new?

Стоит подчеркнуть, что return в процедуре возвращает 9X_proc из лексически включающего метода, то есть 9X_lambdaexpression метода, в котором была создана процедура, не метода, вызвавшего процедуру . Это следствие 9X_proc закрывающего свойства procs. Итак, следующий 9X_generic-lambda код ничего не выводит:

def foo proc = Proc.new{return} foobar(proc) puts 'foo' end def foobar(proc) proc.call puts 'foobar' end foo 

Хотя процедура выполняется 9X_lambda-functions в foobar, она была создана в foo, поэтому return выходит 9X_lambda-expressions из foo, а не только из foobar. Как писал выше Чарльз 9X_lambda-functions Колдуэлл, в нем есть чувство GOTO. На мой 9X_generic-lambda взгляд, return подходит для блока, который выполняется 9X_lambda-functions в его лексическом контексте, но гораздо 9X_lambda-expressions менее интуитивно понятен при использовании 9X_generic-lambda в процессе, который выполняется в другом 9X_lambda-expressions контексте.

3
0

Ответ #14

Ответ на вопрос: Когда использовать лямбду, когда использовать Proc.new?

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

1
1

  • Для обновления: теперь процессы можно создавать ...