У Ruby 1.8 є тонкі відмінності між proc / lambda, з одного боку, і Proc.new
з іншого.
- У чому ці відмінності?
- Чи можете ви дати рекомендації, як вирішити, який вибрати?
- У Ruby 1.9, proc і lambda різні. Яка угода?
У Ruby 1.8 є тонкі відмінності між proc / lambda, з одного боку, і Proc.new
з іншого.
Відповіді:
Ще одна важлива, але тонка відмінність між програмами, створеними за допомогою, lambda
та програмами, створеними за допомогою, Proc.new
- це те, як вони обробляють return
оператор:
lambda
-create PRO, return
оператор повертається лише з самого procProc.new
процесорі 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 line
return "Jason"
end
whowouldwin
#=> "Jason"
Тепер ось створений Proc.new
процесор 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
при прийнятті путь.
proc
метод. Це просто скорочення Proc.new
?
proc
рівнозначноProc.new
proc
як lambda
і не подобається Proc.new
стосовно повернень. Це означає, що рубіновий документ є неточним.
proc
діє як lambda
у 1.8, але діє як Proc.new
у 1.9. Дивіться відповідь Пітера Вагенета.
lambda
- анонімний метод. Оскільки це метод, він повертає значення, а метод, який його викликав, може робити з ним все, що завгодно, включаючи його ігнорування та повернення іншого значення. A Proc
- це як вставлення у фрагмент коду. Це не так, як метод. Отже, коли повернення відбувається в межах Proc
, це лише частина коду методу, який його викликав.
Для подальшого роз'яснення:
Джоуї говорить, що поведінка повернення Proc.new
здивує. Однак якщо ви вважаєте, що Proc.new поводиться як блок, це не дивно, оскільки саме так ведуть себе блоки. лямби з іншого боку поводяться більше як методи.
Це насправді пояснює, чому Procs є гнучким, коли мова йде про арність (кількість аргументів), тоді як лямбда - це не так. Блоки не вимагають надання всіх їх аргументів, але методи (якщо не передбачено за замовчуванням). Хоча надання аргументу lambda за замовчуванням не є варіантом у Ruby 1.8, він тепер підтримується в Ruby 1.9 з альтернативним синтаксисом лямбда (як зазначає webmat):
concat = ->(a, b=2){ "#{a}#{b}" }
concat.call(4,5) # => "45"
concat.call(1) # => "12"
І Мічіель де Маре (ОП) неправильно ставиться до Проків і лямбда поводиться однаково з суровістю в Рубі 1.9. Я переконався, що вони все ще підтримують поведінку від 1,8, як зазначено вище.
break
твердження насправді не мають великого сенсу ні в Програмах, ні в лямбдах. У Procs перерва поверне вас із Proc.new, яка вже завершена. І не має сенсу відриватися від лямбда, оскільки це по суті метод, і ви ніколи не відірветесь від верхнього рівня методу.
next
, redo
і raise
поводитись однаково як в Програмах, так і в лямбдах. Тоді як retry
це заборонено в будь-якому випадку, і це призведе до виключення.
І нарешті, proc
метод ніколи не слід застосовувати, оскільки він є непослідовним і має несподівану поведінку. У Ruby 1.8 він фактично повертає лямбда! У Ruby 1.9 це було виправлено, і він повертає Proc. Якщо ви хочете створити Proc, дотримуйтесь Proc.new
.
Для отримання додаткової інформації я настійно рекомендую мову програмування The Ruby O'Reilly, яка є моїм джерелом для більшості цієї інформації.
break
з Procs підвищується LocalJumpError
, в той час як break
з лямбда поводяться так само , як return
( тобто , return nil
).
Я знайшов цю сторінку, яка показує, у чому різниця між Proc.new
і в чому lambda
полягає. За словами сторінки, різниця полягає лише в тому, що лямбда суворо до кількості аргументів, які вона приймає, тоді як Proc.new
перетворює відсутні аргументи на nil
. Ось приклад сеансу IRB, що ілюструє різницю:
irb (основний): 001: 0> l = лямбда {| x, y | x + y} => # <Proc: 0x00007fc605ec0748 @ (irb): 1> irb (основна): 002: 0> p = Proc.new {| x, y | x + y} => # <Proc: 0x00007fc605ea8698 @ (irb): 2> irb (main): 003: 0> l.call "привіт", "world" => "helloworld" irb (main): 004: 0> p.call "привіт", "world" => "helloworld" irb (main): 005: 0> l.call "привіт" ArgumentError: неправильна кількість аргументів (1 для 2) від (irb): 1 від (irb): 5: у `call ' від (irb): 5 від: 0 irb (main): 006: 0> p.call "привіт" TypeError: не вдається перетворити нуль у String від (irb): 2: у `+ ' від (irb): 2 від (irb): 6: у `call ' від (irb): 6 від: 0
Сторінка також рекомендує використовувати лямбда, якщо ви конкретно не хочете поводження з помилками. Я згоден з цим настроєм. Використання лямбда здається вадким більш лаконічним, і з такою незначною різницею, здається, кращий вибір у середній ситуації.
Що стосується Ruby 1.9, вибачте, я ще не заглянув у 1.9, але не думаю, що вони все б так змінили (не сприймайте мого слова, хоча, здається, ви чули про якісь зміни, тож Я, мабуть, помиляюся там).
Програма є старшою, але семантика повернення для мене є дуже протиконтактною (принаймні, коли я вивчав мову), оскільки:
Лямбда функціонально безпечніше і простіше міркувати - я завжди використовую її замість проц.
Я не можу сказати багато про тонкі відмінності. Однак можу зазначити, що Ruby 1.9 тепер дозволяє додаткові параметри для лямбда і блоків.
Ось новий синтаксис для стабільних лямбдів під 1.9:
stabby = ->(msg='inside the stabby lambda') { puts msg }
У Ruby 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, ознайомтеся з цією статтею (безсоромне самореклама).
Коротка відповідь: Важливо те, що return
робить: лямбда повертається із себе, а прок повертається із себе ТА функції, яка її викликала.
Менш зрозуміло, чому ви хочете використовувати кожен. лямбда - це те, що ми очікуємо, що це має робити у сенсі функціонального програмування. Це в основному анонімний метод, з поточним обсягом, автоматично пов'язаним. З цих двох, лямбда - це той, який ви, мабуть, використовуєте.
Proc, з іншого боку, дійсно корисний для реалізації самої мови. Наприклад, ви можете реалізувати з ними оператори "if" або "for". Будь-яке повернення, знайдене в proc, повернеться із методу, який його викликав, а не з просто "if". Ось як працюють мови, як працюють висловлювання "якщо", тож я здогадуюсь, що Рубі використовує це під прикриттями, і вони просто викрили це, тому що це здалося потужним.
Це вам справді знадобиться лише в тому випадку, якщо ви створюєте нові мовні конструкції, такі як цикли, конструкції if-else тощо.
Я не помітив жодних коментарів до третього методу в квестоні, "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
proc
повернув лямбда в 1,8; тепер було зафіксовано повернення прок у 1.9 - однак це є надзвичайною зміною; тому більше не рекомендується використовувати
Закриття в Ruby - це хороший огляд того, як в Ruby працюють Ruby, лямбда і Proc з Ruby.
лямбда працює як слід, як і в інших мовах.
Провід 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 створюється лямбда, тоді як у Ruby 1.9+ поводиться так Proc.new
, що насправді заплутано.
Щоб детальніше ознайомитись з відповіддю хлопця-акордеона
Зауважте, що Proc.new
створюється проклейка, передаючи блок. Я вважаю, що lambda {...}
це аналізується як якийсь буквальний, а не виклик методу, який передає блок. return
Якщо внутрішній блок, приєднаний до виклику методу, повернеться з методу, а не з блоку, і Proc.new
випадок є прикладом цього при програванні.
(Це 1,8. Я не знаю, як це означає 1.9.)
Я трохи запізнююсь на цьому, але є одна чудова, але маловідома річ про те, що 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!
&block
аргумент у def
, але без цього потрібно робити у списку def arg.
Варто підкреслити, що return
в proc повертається із лексично огороджувального методу, тобто методу, де створено proc , а не методу, який називав proc. Це наслідок властивості закриття проектів. Отже, наступний код нічого не видає:
def foo
proc = Proc.new{return}
foobar(proc)
puts 'foo'
end
def foobar(proc)
proc.call
puts 'foobar'
end
foo
Хоча Proc виконується в foobar
, він був створений в foo
і тому return
виходи foo
, а не просто foobar
. Як Чарльз Колдвелл писав вище, це відчуває GOTO. На мою думку, return
прекрасно в блоці, який виконується в його лексичному контексті, але набагато менш інтуїтивно зрозумілий при використанні в програмі, який виконується в іншому контексті.
Різниця в поведінці з return
IMHO є найважливішою відмінністю між 2. Я також віддаю перевагу лямбда, тому що вона менш типізована, ніж Proc.new :-)
proc {}
. Я не впевнений, коли це набуло чинності, але це (трохи) простіше, ніж вводити Proc.new.