См. Также книгу Ruby Programming Language от Matz и Flanagan, в которой подробно освещена эта тема. proc ведет себя как семантика блока - yield, где лямбда ведет себя как семантика вызова метода. И возвращайся, ломайся, эт. все ведут себя по-разному в procs n lambdas
вы приняли ответ, который говорит только о том, в чем разница между proc и lambda, в то время как заголовок вашего вопроса - когда использовать эти вещи
Шри
Ответы:
378
Другое важное, но тонкое различие между процессами, созданными с помощью, lambdaи процессами, созданными с помощью, Proc.newзаключается в том, как они обрабатывают returnоператор:
В lambda-created proc, returnоператор возвращается только из самого proc
В Proc.new-created proc, returnоператор немного более удивителен: он возвращает контроль не только от proc, но и от метода, включающего proc!
Вот lambdaсозданный процесс returnв действии. Он ведет себя так, как вы, вероятно, ожидаете:
def whowouldwin
mylambda = lambda {return"Freddy"}
mylambda.call
# mylambda gets called and returns "Freddy", and execution# continues on the next linereturn"Jason"end
whowouldwin
#=> "Jason"
Теперь вот Proc.newсозданный proc returnделает то же самое. Вы увидите один из тех случаев, когда Ruby нарушает хваленый принцип наименьшего сюрприза:
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"
Благодаря этому удивительному поведению (а также меньшему количеству печати) я предпочитаю использовать lambdaover Proc.newпри создании процедур.
@mattdipasquale В моих тестах procдействует как lambdaи не как в Proc.newотношении операторов возврата. Это означает, что рубиновый документ является неточным.
Кельвин
31
@mattdipasquale Извините, я был наполовину прав. procдействует как lambdaв 1.8, но действует как Proc.newв 1.9. Смотрите ответ Питера Вагенета.
Кельвин
55
Почему это «удивительное» поведение? А lambdaэто анонимный метод. Поскольку это метод, он возвращает значение, и вызвавший его метод может делать с ним все, что захочет, в том числе игнорировать его и возвращать другое значение. А Procэто как вставка во фрагмент кода. Это не действует как метод. Поэтому, когда происходит возврат в пределах Proc, это только часть кода метода, который его вызвал.
Arcolye
96
Для дальнейшего уточнения:
Джои говорит, что обратное поведение Proc.newудивительно. Однако, если учесть, что Proc.new ведет себя как блок, это неудивительно, поскольку именно так ведут себя блоки. ламбы, с другой стороны, ведут себя больше как методы.
Это фактически объясняет, почему Procs гибки, когда дело доходит до arity (количество аргументов), тогда как лямбды - нет. Блоки не требуют предоставления всех своих аргументов, а методы - (если не указано значение по умолчанию). Хотя предоставление аргумента лямбда по умолчанию не является опцией в Ruby 1.8, теперь он поддерживается в Ruby 1.9 с альтернативным синтаксисом лямбды (как отмечено в webmat):
И Мишель де Маре (ОП) не прав насчет проков и лямбда, которые ведут себя одинаково с arity в 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.
Для получения дополнительной информации я настоятельно рекомендую язык программирования Ruby О'Рейли, который является источником большей части этой информации.
"" "Однако, если учесть, что Proc.new ведет себя как блок, это неудивительно, поскольку именно так ведут себя блоки." "" <- блок является частью объекта, а Proc.new создает объект. И lambda, и Proc.new создают объект, класс которого - Proc, почему diff?
слабый
1
В рубине 2.5, breakиз Procs повышается LocalJumpError, в то время как breakиз лямбд ведут себя так же , как return( то есть , return nil).
Маса Сакано
43
Я нашел эту страницу, которая показывает, какая разница между Proc.newи lambdaесть. Согласно странице, единственное отличие состоит в том, что лямбда строго определяет количество аргументов, которые она принимает, тогда как Proc.newпреобразует отсутствующие аргументы в nil. Вот пример сеанса IRB, иллюстрирующий разницу:
irb (основной): 001: 0> l = лямбда {| x, y | х + у}
=> # <Proc: 0x00007fc605ec0748 @ (irb): 1>
irb (основной): 002: 0> p = Proc.new {| x, y | х + у}
=> # <Proc: 0x00007fc605ea8698 @ (irb): 2>
irb (основной): 003: 0> l.call "привет", "мир"
=> "helloworld"
irb (main): 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 "привет"
Ошибка типа: невозможно преобразовать ноль в строку
из (irb): 2: в `+ '
из (irb): 2
из (irb): 6: в "вызове"
из (irb): 6
от: 0
На этой странице также рекомендуется использовать лямбду, если вы не хотите, чтобы поведение было устойчивым к ошибкам. Я согласен с этим мнением. Использование лямбды кажется немного более кратким, и с таким незначительным отличием это кажется лучшим выбором в средней ситуации.
Что касается Ruby 1.9, извините, я еще не изучал 1.9, но я не думаю, что они сильно это изменят (хотя, не поверьте мне на слово, кажется, вы слышали о некоторых изменениях, поэтому Я наверное там не прав).
"" "Proc.new преобразует отсутствующие аргументы в ноль" "" Proc.new также игнорирует дополнительные аргументы (конечно, lambda жалуется на это с ошибкой).
слабый
16
Proc старше, но семантика возврата очень противоречит мне (по крайней мере, когда я изучал язык), потому что:
Если вы используете proc, вы, скорее всего, используете какую-то функциональную парадигму.
Proc может вернуться из области действия (см. Предыдущие ответы), что в принципе и является весьма нефункциональным по своей природе.
Лямбда функционально безопаснее и легче рассуждать - я всегда использую ее вместо proc.
Дополнительные параметры в лямбде были очень нужны, я рад, что они добавили их в 1.9. Я предполагаю, что блоки также могут иметь дополнительные параметры тогда (в 1.9)?
mpd
вы не демонстрируете параметры по умолчанию в блоках, только лямбды
iconoclast
11
Короткий ответ: важно то, что returnделает: лямбда возвращается из себя, а proc возвращает из себя и функцию, которая ее вызывала.
Менее понятно, почему вы хотите использовать каждый из них. Лямбда - это то, что мы ожидаем, что вещи должны делать в смысле функционального программирования. Это в основном анонимный метод с текущей областью, автоматически связанной. Из двух, лямбда - это та, которую вы, вероятно, должны использовать.
Proc, с другой стороны, действительно полезен для реализации самого языка. Например, вы можете реализовать операторы if или for для них. Любое возвращение, найденное в процедуре, будет возвращено из вызвавшего его метода, а не просто из оператора «if». Вот как работают языки, как работают операторы «если», так что я предполагаю, что Руби использует это под прикрытием, и они просто разоблачили это, потому что это казалось мощным.
Это действительно понадобится вам, если вы создаете новые языковые конструкции, такие как циклы, конструкции if-else и т. Д.
«Лямбда возвращается из себя, а proc возвращает из себя И функцию, которая ее вызывала» - это совершенно неверное и очень распространенное недоразумение. Proc является замыканием и возвращает метод, который его создал. Смотрите мой полный ответ в другом месте на странице.
ComDubh
10
Хороший способ убедиться в том, что лямбда-выражения выполняются в собственной области видимости (как если бы это был вызов метода), в то время как Procs можно рассматривать как выполняемый inline с вызывающим методом, по крайней мере, это хороший способ решить, какой из них использовать в каждом случае.
Мац заявил, что планирует отказаться от него, потому что сбивает с толку, когда proc и Proc.new возвращают разные результаты. В 1.9 они ведут себя одинаково (proc - это псевдоним Proc.new). eigenclass.org/hiki/Changes+in+Ruby+1.9#l47
Дейв
@banister: procвернул лямбду в 1.8; теперь исправлено возвращение proc в 1.9 - однако это серьезное изменение; следовательно, не рекомендуется использовать больше
Gishu
Я думаю, что кирка где-то в сноске говорит, что процесс фактически ограничен или что-то в этом роде. У меня нет точного номера страницы.
Дертони
7
Замыкания в Ruby - это хороший обзор того, как блоки, лямбда и proc работают в Ruby с Ruby.
Я перестал читать это после того, как прочел «функция не может принимать несколько блоков - нарушая принцип, что замыкания можно свободно передавать как значения». Блоки не являются замыканиями. Процедуры есть, и функция может принимать несколько процедур.
ComDubh
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 (0for1)
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,2ArgumentError: wrong number of arguments (2for1)
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 создает лямбду, в то время как в Ruby 1.9+ ведет себя как Proc.new, что действительно сбивает с толку.
Обратите внимание, что Proc.newсоздается процесс, передавая блок. Я считаю, что lambda {...}это синтаксический анализ, а не вызов метода, который передает блок. returnИзнутри блок, присоединенный к вызову метода, вернется из метода, а не из блока, и Proc.newпримером этого является случай.
Интересно, что он делает то же самое, что и объявление &blockаргумента в def, но без необходимости делать это в списке def arg.
jrochkind
2
Стоит подчеркнуть, что returnв процедуре возвращается лексически заключенный метод, т. Е. Метод, в котором он был создан , а не метод, вызвавший процедуру. Это является следствием свойства замыкания процедур. Поэтому следующий код ничего не выводит:
Хотя процесс запускается в foobar, он был создан в fooи поэтому returnвыходы foo, а не только foobar. Как Чарльз Колдуэлл писал выше, у этого есть GOTO чувство к этому. На мой взгляд, returnэто нормально в блоке, который выполняется в его лексическом контексте, но гораздо менее интуитивно понятен при использовании в процедуре, которая выполняется в другом контексте.
Разница в поведении с returnIMHO является наиболее важной разницей между 2. Я также предпочитаю лямбду, потому что она меньше печатает, чем Proc.new :-)
Для обновления: Procs теперь могут быть созданы с помощью proc {}. Я не уверен, когда это вступило в силу, но это (немного) проще, чем вводить Proc.new.
Ответы:
Другое важное, но тонкое различие между процессами, созданными с помощью,
lambda
и процессами, созданными с помощью,Proc.new
заключается в том, как они обрабатываютreturn
оператор:lambda
-created proc,return
оператор возвращается только из самого procProc.new
-created proc,return
оператор немного более удивителен: он возвращает контроль не только от proc, но и от метода, включающего proc!Вот
lambda
созданный процессreturn
в действии. Он ведет себя так, как вы, вероятно, ожидаете:Теперь вот
Proc.new
созданный procreturn
делает то же самое. Вы увидите один из тех случаев, когда Ruby нарушает хваленый принцип наименьшего сюрприза:Благодаря этому удивительному поведению (а также меньшему количеству печати) я предпочитаю использовать
lambda
overProc.new
при создании процедур.источник
proc
метод. Это просто сокращение дляProc.new
?proc
эквивалентноProc.new
proc
действует какlambda
и не как вProc.new
отношении операторов возврата. Это означает, что рубиновый документ является неточным.proc
действует какlambda
в 1.8, но действует какProc.new
в 1.9. Смотрите ответ Питера Вагенета.lambda
это анонимный метод. Поскольку это метод, он возвращает значение, и вызвавший его метод может делать с ним все, что захочет, в том числе игнорировать его и возвращать другое значение. АProc
это как вставка во фрагмент кода. Это не действует как метод. Поэтому, когда происходит возврат в пределахProc
, это только часть кода метода, который его вызвал.Для дальнейшего уточнения:
Джои говорит, что обратное поведение
Proc.new
удивительно. Однако, если учесть, что Proc.new ведет себя как блок, это неудивительно, поскольку именно так ведут себя блоки. ламбы, с другой стороны, ведут себя больше как методы.Это фактически объясняет, почему Procs гибки, когда дело доходит до arity (количество аргументов), тогда как лямбды - нет. Блоки не требуют предоставления всех своих аргументов, а методы - (если не указано значение по умолчанию). Хотя предоставление аргумента лямбда по умолчанию не является опцией в Ruby 1.8, теперь он поддерживается в Ruby 1.9 с альтернативным синтаксисом лямбды (как отмечено в webmat):
И Мишель де Маре (ОП) не прав насчет проков и лямбда, которые ведут себя одинаково с arity в 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
.Для получения дополнительной информации я настоятельно рекомендую язык программирования Ruby О'Рейли, который является источником большей части этой информации.
источник
break
из Procs повышаетсяLocalJumpError
, в то время какbreak
из лямбд ведут себя так же , какreturn
( то есть ,return nil
).Я нашел эту страницу, которая показывает, какая разница между
Proc.new
иlambda
есть. Согласно странице, единственное отличие состоит в том, что лямбда строго определяет количество аргументов, которые она принимает, тогда какProc.new
преобразует отсутствующие аргументы вnil
. Вот пример сеанса IRB, иллюстрирующий разницу:На этой странице также рекомендуется использовать лямбду, если вы не хотите, чтобы поведение было устойчивым к ошибкам. Я согласен с этим мнением. Использование лямбды кажется немного более кратким, и с таким незначительным отличием это кажется лучшим выбором в средней ситуации.
Что касается Ruby 1.9, извините, я еще не изучал 1.9, но я не думаю, что они сильно это изменят (хотя, не поверьте мне на слово, кажется, вы слышали о некоторых изменениях, поэтому Я наверное там не прав).
источник
Proc старше, но семантика возврата очень противоречит мне (по крайней мере, когда я изучал язык), потому что:
Лямбда функционально безопаснее и легче рассуждать - я всегда использую ее вместо proc.
источник
Я не могу сказать много о тонких различиях. Однако я могу отметить, что в Ruby 1.9 теперь разрешены необязательные параметры для лямбд и блоков.
Вот новый синтаксис для стабильных лямбд под 1.9:
В Ruby 1.8 такой синтаксис отсутствовал. Ни один из традиционных способов объявления блоков / лямбд не поддерживал необязательные аргументы:
Ruby 1.9, однако, поддерживает необязательные аргументы даже со старым синтаксисом:
Если вы хотите собрать Ruby1.9 для Leopard или Linux, ознакомьтесь с этой статьей (бесстыдная самореклама).
источник
Короткий ответ: важно то, что
return
делает: лямбда возвращается из себя, а proc возвращает из себя и функцию, которая ее вызывала.Менее понятно, почему вы хотите использовать каждый из них. Лямбда - это то, что мы ожидаем, что вещи должны делать в смысле функционального программирования. Это в основном анонимный метод с текущей областью, автоматически связанной. Из двух, лямбда - это та, которую вы, вероятно, должны использовать.
Proc, с другой стороны, действительно полезен для реализации самого языка. Например, вы можете реализовать операторы if или for для них. Любое возвращение, найденное в процедуре, будет возвращено из вызвавшего его метода, а не просто из оператора «if». Вот как работают языки, как работают операторы «если», так что я предполагаю, что Руби использует это под прикрытием, и они просто разоблачили это, потому что это казалось мощным.
Это действительно понадобится вам, если вы создаете новые языковые конструкции, такие как циклы, конструкции if-else и т. Д.
источник
Хороший способ убедиться в том, что лямбда-выражения выполняются в собственной области видимости (как если бы это был вызов метода), в то время как Procs можно рассматривать как выполняемый inline с вызывающим методом, по крайней мере, это хороший способ решить, какой из них использовать в каждом случае.
источник
Я не заметил никаких комментариев по поводу третьего метода в квесте, «proc», который устарел, но обрабатывается по-разному в 1.8 и 1.9.
Вот довольно подробный пример, который позволяет легко увидеть различия между тремя подобными вызовами:
источник
proc
вернул лямбду в 1.8; теперь исправлено возвращение proc в 1.9 - однако это серьезное изменение; следовательно, не рекомендуется использовать большеЗамыкания в Ruby - это хороший обзор того, как блоки, лямбда и proc работают в Ruby с Ruby.
источник
Лямбда работает как положено, как и на других языках.
Проводной
Proc.new
является удивительным и запутанным.return
Заявление в прок , созданныйProc.new
не только возвращает управление только от себя, но и от метода заключая его .Вы можете утверждать, что
Proc.new
вставляет код в метод, как блок. НоProc.new
создает объект, в то время как блок является частью объекта.И есть еще одно различие между лямбда и
Proc.new
, которое заключается в их обработке (неправильных) аргументов. Лямбда жалуется на это,Proc.new
игнорируя при этом дополнительные аргументы или рассматривая отсутствие аргументов как ноль.Кстати,
proc
в Ruby 1.8 создает лямбду, в то время как в Ruby 1.9+ ведет себя какProc.new
, что действительно сбивает с толку.источник
Чтобы уточнить ответ аккордеонного парня:
Обратите внимание, что
Proc.new
создается процесс, передавая блок. Я считаю, чтоlambda {...}
это синтаксический анализ, а не вызов метода, который передает блок.return
Изнутри блок, присоединенный к вызову метода, вернется из метода, а не из блока, иProc.new
примером этого является случай.(Это 1,8. Я не знаю, как это переводится в 1,9.)
источник
Я немного опоздал на это, но есть одна замечательная, но малоизвестная вещь, о которой
Proc.new
вообще не упоминается в комментариях. По документации :Тем не менее,
Proc.new
давайте объединить методы:источник
&block
аргумента вdef
, но без необходимости делать это в списке def arg.Стоит подчеркнуть, что
return
в процедуре возвращается лексически заключенный метод, т. Е. Метод, в котором он был создан , а не метод, вызвавший процедуру. Это является следствием свойства замыкания процедур. Поэтому следующий код ничего не выводит:Хотя процесс запускается в
foobar
, он был создан вfoo
и поэтомуreturn
выходыfoo
, а не толькоfoobar
. Как Чарльз Колдуэлл писал выше, у этого есть GOTO чувство к этому. На мой взгляд,return
это нормально в блоке, который выполняется в его лексическом контексте, но гораздо менее интуитивно понятен при использовании в процедуре, которая выполняется в другом контексте.источник
Разница в поведении с
return
IMHO является наиболее важной разницей между 2. Я также предпочитаю лямбду, потому что она меньше печатает, чем Proc.new :-)источник
proc {}
. Я не уверен, когда это вступило в силу, но это (немного) проще, чем вводить Proc.new.