Kdy použít lambda, kdy je třeba použít Proc.new?

hlasů
316

V Ruby 1.8, existují jemné rozdíly mezi proc / lambda na jedné straně a Proc.newna straně druhé.

  • Jaké jsou tyto rozdíly?
  • Můžete dát pokyny, jak se rozhodnout, který z nich si vybrat?
  • V Ruby 1.9, proc a lambda jsou různé. O co jde?
Položena 03/08/2008 v 07:40
zdroj uživatelem
V jiných jazycích...                            


15 odpovědí

hlasů
364

Dalším důležitým, ale jemný rozdíl mezi procs vytvořených lambdaa procs vytvořen Proc.newje, jak zvládnout returnprohlášení:

  • V lambda-created proc je returnpříkaz vrátí pouze ze samotného proc
  • V Proc.new-created proc je returntvrzení je poněkud překvapující: je to vrátí řízení nejen z proc, ale také ze způsobu obklopující proc!

Zde je lambda-created proc je returnv akci. Chová se tak, že budete pravděpodobně očekávat:

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"

Právě tady je Proc.new-created proc to returndělá to samé. Jsi asi vidět jeden z těch případů, kdy Ruby zlomí hodně vynášené Princip nejmenšího překvapení:

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"

Díky tomuto překvapivému chování (stejně jako méně psaní), mám tendenci upřednostňovat používání lambdavíce než Proc.newpři výrobě procs.

Odpovězeno 03/08/2008 v 16:21
zdroj uživatelem

hlasů
93

Poskytnout další vysvětlení:

Joey říká, že návrat chování Proc.newje překvapivá. Nicméně, když si uvědomíte, že Proc.new chová jako blok To není překvapivé, protože je to přesně tak, jak se chovají bloky. lambas naopak chovají spíš jako metody.

To vysvětluje, proč vlastně Procesy jsou flexibilní, pokud jde o arity (počet argumentů), zatímco Lambdy nejsou. Bloky nepotřebují všechny jejich argumenty, které mají být poskytnuty, ale metody dělat (pokud není k dispozici výchozí). Při poskytování lambda argumentu výchozí není možnost v Ruby 1.8 je nyní podporován v Ruby 1.9 s alternativní syntaxe lambda (jak poznamenal webmat):

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

A Michiel de Mare (dále jen OP) je nesprávná o PROC a lambda chovají stejně s arity v Ruby 1.9. Mám ověřeno, že stále udržují chování v rozmezí od 1,8, jak je uvedeno výše.

breakProhlášení nemají ve skutečnosti moc smysl buď ve PROC nebo lambd. V PROC, break vás vrátí z Proc.new, která již byla dokončena. A to nedává žádný smysl zlomit z lambda, protože je to v podstatě způsob, a vy byste nikdy zlomit z nejvyšší úrovně metody.

next, redoA raisechovají stejně v obou PROC a lambd. Vzhledem k tomu retrynení povoleno v jedné a bude vyvolat výjimku.

A konečně, procmetoda by měla být nikdy použita jako taková je v rozporu a má neočekávané chování. Ruby 1.8 je ve skutečnosti vrátí lambda! V Ruby 1,9 to bylo pevné a vrací Proc. Chcete-li vytvořit Proc, držet se Proc.new.

Pro více informací, vřele doporučuji O'Reilly Ruby programovací jazyk , který je mým zdrojem pro většinu z těchto informací.

Odpovězeno 04/10/2009 v 06:23
zdroj uživatelem

hlasů
41

Našel jsem tuto stránku , která ukazuje, jaký je rozdíl mezi Proc.newa lambdajsou. Podle stránky, jediný rozdíl je, že lambda je přísný o počtu argumentů je přijímá, zatímco Proc.newkonvertité chybí argumenty nil. Zde je příklad IRB relace ilustrující rozdíl:

IRB (hlavní): 001: 0> l = lambda {| x, y | x + y}
=> # <Proc: 0x00007fc605ec0748 @ (IRB): 1>
IRB (hlavní): 002: 0> p = Proc.new {| x, y | x + y}
=> # <Proc: 0x00007fc605ea8698 @ (IRB): 2>
IRB (hlavní): 003: 0> l.call "ahoj", "svět"
=> "HelloWorld"
IRB (hlavní): 004: 0> p.call "ahoj", "svět"
=> "HelloWorld"
IRB (hlavní): 005: 0> l.call "Dobrý den"
ArgumentError: chybný počet argumentů (1 pro 2 osoby)
    z (IRB): 1
    od (IRB): 5: v `výzva‘
    z (IRB): 5
    od: 0
IRB (hlavní): 006: 0> p.call "ahoj"
TypeError: nelze převést nil na provázku
    z (IRB): 2: v `+‘
    z (IRB): 2
    od (IRB): 6: `výzva‘
    z (IRB): 6
    od: 0

Stránka také doporučuje používat lambda pokud si výslovně přejete tolerantní chování chyby. Souhlasím s tímto sentimentem. Použitím lambda se zdá o něco přesnější a s takovou nevýznamné rozdíly, zdá se, že lepší volbou průměrné situaci.

Pokud jde o Ruby 1.9, promiň, já jsem se podíval do 1,9 zatím, ale neumím si představit, že by ji změnit tak moc (neberte mé slovo pro to však, zdá se, že jste slyšeli o některé změny, tak jsem asi špatně zde).

Odpovězeno 03/08/2008 v 08:28
zdroj uživatelem

hlasů
14

Proc je starší, ale sémantika návratu jsou velmi neintuitivní ke mně (alespoň když jsem se učil jazyk), protože:

  1. Pokud používáte proc jste se s největší pravděpodobností používat nějaký funkční paradigmatu.
  2. Proc může vrátit z vkládací rozsahu (viz předchozí reakce), který je v podstatě Goto a vysoce nefunkční v přírodě.

Lambda je funkčně bezpečnější a jednodušší uvažovat o - vždycky jsem ji používat namísto proc.

Odpovězeno 11/09/2008 v 00:32
zdroj uživatelem

hlasů
11

Nemohu říci nic o jemné rozdíly. Nicméně se může poukázat na to, že Ruby 1.9 nyní umožňuje volitelné parametry pro lambd a bloků.

Zde je nová syntaxe pro Stabby lambd pod 1,9:

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

Ruby 1.8 neměl tuto syntaxi. Ani konvenční způsob deklarovat bloků / lambd podporují volitelné args:

# 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, nicméně, podporuje volitelné argumenty, i když byly staré syntaxe:

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

Jestli chceš postavit Ruby1.9 pro Leopard nebo Linux, podívejte se na tento článek (nestydatá vlastní propagace).

Odpovězeno 19/11/2008 v 22:28
zdroj uživatelem

hlasů
10

Stručná odpověď: Na čem záleží, je to, co returndělá: lambda vrací ven sám, a proc se vrátí ven sám a funkce, která jej nazývá.

Co je méně jasné je, proč chcete používat každý. lambda je to, co očekáváme, že by se věci měly dělat ve funkčním programovacím smyslu. Je to v podstatě anonymní metoda se současným rozsahem automaticky vázán. Z těchto dvou lambda je ten, který by měl být pravděpodobně používat.

Proc, na druhou stranu, je velmi užitečné pro realizaci samotného jazyka. Například můžete implementovat „kdyby“ prohlášení nebo „pro“ smyčky s nimi. Jakýkoliv návrat nalézt v proc se vrátí ze způsobu, který ji volal, nikoli jen „kdyby“ prohlášení. To je, jak jazyk funguje, jak „kdyby“ prohlášení pracovat, takže můj odhad je Ruby používá toto pod deku a oni jen vystaveny, protože to vypadalo silný.

Ty by jen opravdu potřebovat, pokud budete vytvářet nové jazykové konstrukty, jako smyčky, if-else konstrukcí, atd.

Odpovězeno 06/10/2011 v 19:33
zdroj uživatelem

hlasů
9

Dobrým způsobem, jak to vidím, je, že Lambdy jsou prováděny ve vlastní působnosti (jako kdyby to bylo volání metody), zatímco Procesy lze považovat za provedený inline s volající metody, alespoň, že je to dobrý způsob, jak rozhodnout, Wich jedno použití v každém případě.

Odpovězeno 09/12/2008 v 15:17
zdroj uživatelem

hlasů
8

Nevšiml jsem si žádné připomínky třetí metody v queston, „proc“, který je zastaralý, ale zachází odlišně v 1,8 a 1,9.

Zde je poměrně podrobného příklad, který usnadňuje vidět rozdíly mezi těmito třemi podobnými výzvy:

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
Odpovězeno 25/06/2009 v 12:22
zdroj uživatelem

hlasů
7

Uzávěry Ruby je dobrý přehled o tom, jak bloky, lambda a proc práce v Ruby, Ruby.

Odpovězeno 28/08/2008 v 14:50
zdroj uživatelem

hlasů
6

Pochopení Ruby tvárnic, PROC a lambdy Robert Sosinski jasně vysvětluje tyto programovací koncepty a posiluje vysvětlení s kódem příkladu. Metoda objekty jsou spojeny a zahrnuty také.

Odpovězeno 23/08/2013 v 18:07
zdroj uživatelem

hlasů
5

lambda pracuje podle očekávání, stejně jako v jiných jazycích.

Kabelový Proc.newje překvapující a matoucí.

returnProhlášení v proc vytvořen Proc.newnejen vrátit řízení jen ze sebe, ale i ze způsobu obklopuje ji .

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

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

Můžete namítnout, že Proc.newvloží kód do metody ohradní, stejně jako blok. Ale Proc.newvytvoří objekt, zatímco blok je součástí objektu.

A je tu další rozdíl mezi lambda a Proc.new, což je jejich vyřizování (špatně) argumenty. lambda stěžuje na to, zatímco Proc.newignoruje další argumenty, nebo se domnívá, že neexistují argumenty jako nulové.

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"

BTW, procv Ruby 1.8 vytvoří lambda, zatímco v Ruby 1.9+ se chová podobně Proc.new, což je opravdu matoucí.

Odpovězeno 29/10/2014 v 16:22
zdroj uživatelem

hlasů
3

Vypracovat na akordeon Guy odpovědi:

Všimněte si, že Proc.newvytvoří proc se tím, že prošel gól. Domnívám se, že lambda {...}je analyzován jako druh doslovný, spíše než volání metody, která prochází blok. returning zevnitř bloku připojeného k volání metody vrátí ze způsobu, ne bloku, a Proc.newpřípad je příkladem toho ve hře.

(To je 1,8. Nevím, jak se to promítá do 1,9).

Odpovězeno 07/09/2008 v 03:31
zdroj uživatelem

hlasů
2

Jsem trochu pozdě na to, ale je zde jedna velká, ale málo známá věc, o Proc.newkteré nejsou uvedeny v komentářích vůbec. Jako by dokumentací :

Proc::newmůže být nazýván bez bloku pouze v rámci metody s připojeným blokem, přičemž v tomto případě, že je blok převede naProc objekt.

To znamená, že Proc.newumožňuje, aby metody řetězových což vede k získání

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!
Odpovězeno 28/04/2015 v 13:15
zdroj uživatelem

hlasů
1

Je třeba zdůraznit, že returnza proc výnosů z lexically vkládací metodou, tedy způsobu, kde byla vytvořena proc , není metoda, která se nazývá proc. Jedná se o důsledek majetku uzavíracím procs. Takže následující kód výstupy nic:

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

def foobar(proc)
  proc.call
  puts 'foobar'
end

foo

I když se spustí proc v foobar, že byl vytvořen v roce foo, a tak se returnvýchody foo, ne jen foobar. Jako Charles Caldwell napsal výše, má GOTO cítí k ní. Podle mého názoru, returnje v pořádku, v bloku, který je proveden ve své lexikální souvislosti, ale je mnohem méně intuitivní když použitý v proc, který je proveden v jiném kontextu.

Odpovězeno 15/11/2018 v 10:00
zdroj uživatelem

hlasů
1

Rozdíl v chování se returnje IMHO nejdůležitější rozdíl mezi 2. Také jsem raději lambda, protože je to méně, než psaní Proc.new :-)

Odpovězeno 11/08/2008 v 03:09
zdroj uživatelem

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