Wanneer lambda nodig heeft, wanneer Proc.new gebruiken?

stemmen
316

In Ruby 1.8 zijn er subtiele verschillen tussen proc / lambda enerzijds, en Proc.newanderzijds.

  • Wat zijn de verschillen?
  • Kun je richtlijnen over hoe om te beslissen welke te kiezen te geven?
  • In Ruby 1.9, proc en lambda zijn verschillend. Wat is er aan de hand?
De vraag is gesteld op 03/08/2008 om 07:40
bron van user
In andere talen...                            


15 antwoorden

stemmen
41

Ik vond deze pagina die laat zien wat het verschil tussen Proc.newen lambdazijn. Volgens de pagina, het enige verschil is dat een lambda is streng over het aantal argumenten accepteert, terwijl Proc.newbekeerlingen argumenten ontbreekt aan nil. Hier is een voorbeeld IRB sessie ter illustratie van het verschil:

irb (main): 001: 0> l = lambda {| x, y | x + y}
=> # <Proc: 0x00007fc605ec0748 @ (IRB): 1>
irb (main): 002: 0> p = Proc.new {| x, y | x + y}
=> # <Proc: 0x00007fc605ea8698 @ (IRB): 2>
irb (hoofd): 003: 0> l.call "hello", "wereld"
=> "Helloworld"
irb (hoofd): 004: 0> p.call "hello", "wereld"
=> "Helloworld"
irb (hoofd): 005: 0> l.call "hello"
ArgumentError: verkeerde aantal argumenten (1 voor 2)
    uit (IRB): 1
    van (IRB): 5: in `call'
    uit (IRB): 5
    uit: 0
irb (hoofd): 006: 0> p.call "hello"
TypeError: kan niet nihil omzetten in String
    uit (IRB): 2: in '+'
    uit (IRB): 2
    van (IRB): 6: in `call'
    uit (IRB): 6
    uit: 0

De pagina beveelt ook het gebruik van lambda, tenzij u specifiek wilt dat de fout tolerant gedrag. Ik ben het eens met dit sentiment. Met behulp van een lambda lijkt een beetje meer beknopte, en met zulk een onbeduidend verschil, lijkt de betere keuze van de gemiddelde situatie.

Zoals voor Ruby 1.9, sorry, ik heb nog niet gekeken naar 1,9, maar ik kan me niet voorstellen dat ze zou veranderen al dat veel (niet mijn woord al te geloven, het lijkt alsof je hebben gehoord van een aantal wijzigingen, zodat ik ben waarschijnlijk verkeerd daar).

antwoordde op 03/08/2008 om 08:28
bron van user

stemmen
364

Een ander belangrijk, maar subtiel verschil tussen procs gemaakt met lambdaen procs gemaakt met Proc.newis hoe ze het omgaan met returnverklaring:

  • In een lambdaërde proc, de returninstructie geeft alleen van de proc zelf
  • In een Proc.newërde proc, de returnverklaring is een beetje vreemd: het de controle keert niet alleen van de proc, maar ook uit de methode bijvoeging van de proc!

Hier is lambdaërde proc's returnin actie. Het gedraagt zich op een manier die je waarschijnlijk verwachten:

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"

Nu hier is een Proc.newërde proc's returnhetzelfde te doen. Je staat op het punt een van die gevallen waarin Ruby breekt de veelgeprezen principe van de minst Surprise te zien:

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"

Dankzij deze verrassende gedrag (en minder typering), heb ik de neiging de voorkeur te geven met behulp van lambdameer dan Proc.newbij het maken van ontwerp en bouw.

antwoordde op 03/08/2008 om 16:21
bron van user

stemmen
1

Het verschil in gedrag returnis IMHO het belangrijkste verschil tussen de 2. Ik heb ook de voorkeur aan lambda, want het is minder te typen dan Proc.new :-)

antwoordde op 11/08/2008 om 03:09
bron van user

stemmen
7

Sluitingen in Ruby is een goed overzicht voor de manier waarop blokken, lambda en proc werk in Ruby, Ruby.

antwoordde op 28/08/2008 om 14:50
bron van user

stemmen
3

Uit te werken op het antwoord van Accordion Guy's:

Merk op dat Proc.neween proc ontstaat door wordt geleid een blok. Ik geloof dat lambda {...}wordt ontleed als een soort letterlijke, in plaats van een aanroep van de methode waarbij een blok passeert. returning vanuit een blok bevestigd aan een methodeaanroep terugkeren van de werkwijze niet het blok, en het Proc.newgeval is een voorbeeld van het spel.

(Dit is 1,8. Ik weet niet hoe dit vertaalt naar 1.9.)

antwoordde op 07/09/2008 om 03:31
bron van user

stemmen
14

Proc is ouder, maar de semantiek van de terugkeer zijn zeer contra-intuïtief voor mij (tenminste toen ik de taal leren) omdat:

  1. Als u gebruik maakt proc, bent u het meest waarschijnlijk met behulp van een soort van functionele paradigma.
  2. Proc kan terugkeren van het omsluitende scope (zie voorgaande reacties), een goto wezen en sterk niet-functioneel karakter.

Lambda is functioneel veiliger en gemakkelijker tot ongeveer redeneren - ik gebruik het altijd in plaats van proc.

antwoordde op 11/09/2008 om 00:32
bron van user

stemmen
11

Ik kan niet veel over de subtiele verschillen te zeggen. Toch kan ik erop wijzen dat Ruby 1.9 maakt het nu mogelijk optionele parameters voor lambda en blokkeert.

Hier is de nieuwe syntaxis voor de stabby lambdas onder 1.9:

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

Ruby 1.8 had dat niet syntax. Ook niet de conventionele manier te verklaren blokken / lambda's aanvaarden optionele argumenten:

# 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 ondersteunt echter optionele argumenten, zelfs met de oude syntax:

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

Als u wilt bouwen ruby1.9 voor Leopard of Linux, kijk op dit artikel (schaamteloze zelfpromotie).

antwoordde op 19/11/2008 om 22:28
bron van user

stemmen
9

Een goede manier om het te zien is dat de lambda's worden uitgevoerd in hun eigen reikwijdte (alsof het een aanroep van de methode), terwijl Procs kan gezien worden als inline uitgevoerd met de roeping methode, althans dat is een goede manier om te bepalen welke u wilt gebruiken in ieder geval.

antwoordde op 09/12/2008 om 15:17
bron van user

stemmen
8

Ik heb geen commentaar op de derde methode in de queston, "proc", die is verouderd, maar anders behandeld in 1,8 en 1,9 opmerken.

Hier is een vrij uitgebreide voorbeeld dat maakt het gemakkelijk om de verschillen tussen de drie soortgelijke gesprekken weer te geven:

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
antwoordde op 25/06/2009 om 12:22
bron van user

stemmen
93

Om meer duidelijkheid te verschaffen:

Joey zegt dat de terugkeer gedrag van Proc.newis verrassend. Maar als je bedenkt dat Proc.new zich gedraagt als een blok is dit niet verwonderlijk want dat is precies hoe straten gedragen. lambas aan de andere kant zich meer als methoden.

Dit verklaart eigenlijk waarom Procs zijn flexibel als het gaat om ariteit (aantal argumenten), terwijl lambda's niet. Blokken hoeven niet al hun argumenten te worden verstrekt, maar methoden doen (tenzij een standaard wordt meegeleverd). Terwijl het verstrekken van lambda argument default is geen optie in Ruby 1.8 wordt nu ondersteund in Ruby 1.9 met de alternatieve lambda syntax (zoals opgemerkt door webmat):

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

En Michiel de Mare (het OP) is onjuist over de Procs en lambda gedragen hetzelfde met ariteit in Ruby 1.9. Ik heb vastgesteld dat ze nog steeds het gedrag te behouden van 1,8, zoals hierboven aangegeven.

breakverklaringen niet echt veel zin in een van beide Procs of lambda. In Procs, zou de pauze je terug komt van Proc.new die reeds is afgerond. En het heeft geen enkele zin om te breken uit een lambda want het is in wezen een methode, en je zou nooit breken van het hoogste niveau van een methode.

next, redoEn raisegedragen zich hetzelfde in beide Procs en lambda. Overwegende dat retryis niet toegestaan in een van beide en zal een uitzondering te verhogen.

En tot slot, de procmethode mag nooit worden gebruikt zoals het is inconsistent en heeft onverwacht gedrag. In Ruby 1.8 het eigenlijk geeft een lambda! In Ruby 1.9 werd dit vastgesteld en retourneert een Proc. Als u wilt een Proc maken, blijf Proc.new.

Voor meer informatie, ik beveel O'Reilly's The Ruby programmeertaal die mijn bron voor het grootste deel van deze informatie.

antwoordde op 04/10/2009 om 06:23
bron van user

stemmen
10

Kort antwoord: Waar het om gaat is wat returndoet: lambda rendement uit zichzelf, en proc keert terug uit zichzelf en de functie die het genoemd.

Wat minder duidelijk is waarom u wilt elk gebruik. lambda is wat we verwachten dat alles moeten doen in een functionele programmeertaal zin. Het is eigenlijk een anonieme methode met de huidige scope automatisch gebonden. Van de twee, lambda is degene die je moet waarschijnlijk worden gebruikt.

Proc, aan de andere kant, is erg handig voor de uitvoering van de taal zelf. U kunt bijvoorbeeld "if" statements of "voor" loops met ze uit te voeren. Een eventuele terugkeer in de proc zal terugkeren uit de methode die het genoemd, niet de alleen de "if" statement. Dit is hoe talen werken, hoe "if" statements werken, dus ik denk Ruby gebruikt deze onder de dekens en ze gewoon blootgesteld omdat het leek krachtig.

Je zou alleen echt nodig als je het creëren van nieuwe taalconstructies als loops, if-else constructies, etc.

antwoordde op 06/10/2011 om 19:33
bron van user

stemmen
6

Inzicht in Ruby Blocks, Procs en Lambda door Robert Sosinski legt duidelijk uit deze programmering concepten en versterkt de uitleg met voorbeeld code. Werkwijze objecten zijn gerelateerd en eveneens bedekt.

antwoordde op 23/08/2013 om 18:07
bron van user

stemmen
5

lambda werkt zoals verwacht, net als in andere talen.

De bedrade Proc.newis verrassend en verwarrend.

De returnverklaring in proc gecreëerd door Proc.newniet alleen de controle terug te keren alleen van zichzelf, maar ook uit de methode omsluit .

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

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

Je kunt stellen dat Proc.newde code wordt ingevoegd in de omsluitende methode, net als blok. Maar Proc.newcreëert een object, terwijl blok van een object.

En er is nog een verschil tussen de lambda en Proc.new, wat hun behandeling van (verkeerde) argumenten. lambda klaagt erover, terwijl Proc.newnegeert extra argumenten of beschouwt het ontbreken van argumenten als nihil.

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"

Trouwens, procin Ruby 1.8 zorgt voor een lambda, terwijl in Ruby 1.9+ gedraagt zich als Proc.new, dat is echt verwarrend.

antwoordde op 29/10/2014 om 16:22
bron van user

stemmen
2

Ik ben een beetje laat op deze, maar er is één groot, maar weinig bekend ding over Proc.newniet in reacties vermeld op alle. Zoals door documentatie :

Proc::newworden genoemd zonder blok slechts bij een werkwijze met een aangesloten blok, waarbij dat blok wordt omgezet in hetProc object.

Dat gezegd hebbende, Proc.newlaat aan de ketting waardoor methoden:

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!
antwoordde op 28/04/2015 om 13:15
bron van user

stemmen
1

Het is de moeite waard te benadrukken dat returnin een proc rendement van de lexicaal omsluitende methode, dat wil zeggen de methode waarbij de proc is gemaakt , niet de methode die de proc genoemd. Dit is een gevolg van de sluiting eigendom van ontwerp en bouw. Dus de volgende code geeft niets:

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

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

foo

Hoewel de proc uitvoert in foobar, werd opgericht in fooen zo de returnuitgangen foo, niet alleen foobar. Zoals Charles Caldwell hierboven schreef, heeft het een GOTO feel to it. Naar mijn mening, returnis prima in een blok dat wordt uitgevoerd in de lexicale context, maar is veel minder intuïtief bij gebruik in een proc, dat wordt uitgevoerd in een andere context.

antwoordde op 15/11/2018 om 10:00
bron van user

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