Når man skal bruke lambda, når du skal bruke Proc.new?

stemmer
319

I Ruby 1,8, er det små forskjeller mellom proc / lambda på den ene side, og Proc.newpå den andre.

  • Hva er disse forskjellene?
  • Kan du gi retningslinjer for hvordan å bestemme hvilken du skal velge?
  • I Ruby 1,9, proc og lambda er forskjellige. Hva er greia?
Publisert på 03/08/2008 klokken 06:40
kilden bruker
På andre språk...                            


15 svar

stemmer
369

En annen viktig, men subtile forskjellen mellom procs opprettet med lambdaog procs opprettet med Proc.newer hvordan de håndterer returnuttalelse:

  • I en lambda-Laget proc, den returnreturnerer uttalelsen bare fra proc selv
  • I en Proc.new-Laget proc, det returner uttalelsen litt mer overraskende: den returnerer kontroll ikke bare fra proc, men også fra metoden som omslutter proc!

Her er lambda-Laget proc er returni aksjon. Den oppfører seg på en måte som du sannsynligvis forvente:

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"

Nå her er en Proc.new-Laget proc er returnå gjøre det samme. Du er i ferd med å se en av de tilfellene der Ruby bryter den mye oppskrytte prinsippet om minst Surprise:

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"

Takket være dette overraskende oppførsel (samt mindre typing), jeg har en tendens til å favorisere hjelp lambdai løpet Proc.newnår du gjør procs.

Svarte 03/08/2008 kl. 15:21
kilden bruker

stemmer
94

For å gi ytterligere avklaring:

Joey sier at avkastningen oppførsel Proc.newer overraskende. Men når du tenker på at Proc.new oppfører seg som en blokk dette er ikke overraskende ettersom det er nøyaktig hvordan blokker oppføre seg. lambas derimot oppfører seg mer som metoder.

Dette forklarer egentlig hvorfor Procs er fleksible når det gjelder å arity (antall argumenter), mens lambdaene ikke. Blokker krever ikke alle deres argumenter for å bli gitt, men metoder gjøre (med mindre en standard er oppgitt). Samtidig som lambda argument standard er ikke et alternativ i Ruby 1,8, er det nå støttet i Ruby 1.9 med den alternative syntaksen lambda (som nevnt av webmat):

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

Og Michiel de Mare (OP) er feil om Procs og lambda oppfører det samme med arity i Ruby 1.9. Jeg har bekreftet at de fortsatt opprettholde atferd fra 1,8 som angitt ovenfor.

breakuttalelser faktisk ikke mye mening i enten Procs eller lambdaene. I Procs, ville pause returnere deg fra Proc.new som allerede er gjennomført. Og det gjør ikke noe fornuftig å bryte fra en lambda siden det er egentlig en metode, og du ville aldri bryte fra det øverste nivået i en metode.

next, redoOg raiseoppfører den samme i begge Procs og lambdaene. Mens retryer ikke tillatt i heller, og vil heve et unntak.

Og til slutt, procbør aldri brukes metoden som det er inkonsekvent og har uventet oppførsel. I Ruby 1.8 det faktisk returnerer en lambda! I Ruby 1,9 dette har blitt fikset, og den returnerer en Proc. Hvis du ønsker å opprette en Proc, stokk med Proc.new.

For mer informasjon, jeg anbefaler O'Reillys programmeringsspråket Ruby som er min kilde for det meste av denne informasjonen.

Svarte 04/10/2009 kl. 05:23
kilden bruker

stemmer
41

Jeg fant denne siden som viser hva forskjellen mellom Proc.newog lambdaer. Ifølge siden, den eneste forskjellen er at en lambda er strenge på antall argumenter det aksepterer, mens Proc.newkonvertitter mangler argumenter til nil. Her er et eksempel IRB sesjon som illustrerer forskjellen:

irb (hoved): 001: 0> l = lambda {| x, y | x + y}
=> # <Proc: 0x00007fc605ec0748 @ (IRB): 1>
irb (hoved): 002: 0> p = Proc.new {| x, y | x + y}
=> # <Proc: 0x00007fc605ea8698 @ (IRB): 2>
IRB (hoved): 003: 0> l.call "hallo", "verden"
=> "Helloworld"
IRB (hoved): 004: 0> p.call "hallo", "verden"
=> "Helloworld"
irb (hoved): 005: 0> l.call "Hallo"
ArgumentError: feil antall argumenter (en for 2)
    fra (IRB): 1
    fra (IRB): 5: i: Kall'
    fra (IRB): 5
    fra: 0
irb (hoved): 006: 0> p.call "Hallo"
Feiltype: kan ikke konvertere null til String
    fra (IRB): 2: i `+'
    fra (IRB): 2
    fra (IRB): 6: i: Kall'
    fra (IRB): 6
    fra: 0

Siden anbefaler også å bruke lambda med mindre du spesielt ønsker feiltolerant atferd. Jeg er enig med denne følelsen. Ved hjelp av en lambda virker en smule mer konsis, og med en slik en ubetydelig forskjell, virker det bedre valg i den gjennomsnittlige situasjonen.

Som for Ruby 1.9, beklager, jeg har ikke sett på 1.9 ennå, men jeg kan ikke tenke meg at de ville endre det så mye (ikke ta mitt ord for det selv, synes det du har hørt om noen endringer, så jeg er sannsynligvis feil der).

Svarte 03/08/2008 kl. 07:28
kilden bruker

stemmer
14

Proc er eldre, men semantikk av avkastningen er svært motstridende til meg (i hvert fall når jeg lærte språket) fordi:

  1. Hvis du bruker proc, er du mest sannsynlig bruke noen form for funksjonell paradigme.
  2. Proc kan gå tilbake ut av den omsluttende ramme (se tidligere svar), som er en goto i utgangspunktet, og høyst ikke-funksjonelt i naturen.

Lambda er funksjonelt tryggere og lettere å resonnere om - Jeg bruker alltid det i stedet for proc.

Svarte 10/09/2008 kl. 23:32
kilden bruker

stemmer
11

Kort svar: Det som teller er hva returngjør: lambda avkastning ut av seg selv, og proc avkastning ut av seg selv og den funksjonen som heter det.

Hva er mindre klart er grunnen til at du ønsker å bruke hver. lambda er hva vi forvente at ting bør gjøre i en funksjonell programmering forstand. Det er i utgangspunktet en anonym metode med dagens omfang automatisk bundet. Av de to, er lambda den du bør nok bruke.

Proc, på den annen side, er veldig nyttig for å gjennomføre selve språket. For eksempel kan du implementere "hvis" uttalelser eller "på" looper med dem. Enhver retur funnet i proc vil returnere ut av den metoden som kalte det, ikke bare "hvis" statement. Dette er hvordan språk fungerer, hvordan "hvis" uttalelser fungerer, så min gjetning er Ruby bruker denne under dyna og de bare utsatt det fordi det virket kraftig.

Du ville bare trenger dette hvis du oppretter nye språkkonstruksjoner som løkker, if-else konstruksjoner, etc.

Svarte 06/10/2011 kl. 18:33
kilden bruker

stemmer
11

Jeg kan ikke si mye om den subtile forskjeller. Jeg kan imidlertid påpeke at Ruby 1.9 nå tillater valgfrie parametere for lambdaene og blokker.

Her er den nye syntaksen for Stabby lambdaene henhold 1.9:

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

Ruby 1.8 hadde ikke det syntaks. Det gjorde heller ikke den konvensjonelle måten å erklære blokker / lambdaene støtte valg 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, derimot, støtter valgfrie argumenter selv med den gamle syntaks:

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

Hvis du ønsker å bygge Ruby1.9 for Leopard eller Linux, sjekk ut denne artikkelen (skamløs egenreklame).

Svarte 19/11/2008 kl. 21:28
kilden bruker

stemmer
9

En god måte å se det er at lambdaene er utført i sin egen omfang (som om det var et metodekall), mens Procs kan betraktes som utført inline med kall metoden, det er en god måte å bestemme hvilken som å bruke minst i hvert tilfelle.

Svarte 09/12/2008 kl. 14:17
kilden bruker

stemmer
8

Jeg la ikke merke til noen kommentarer på den tredje metoden i queston, "proc", som er foreldet, men håndtert annerledes i 1.8 og 1.9.

Her er en ganske detaljert eksempel som gjør det lett å se forskjellene mellom de tre tilsvarende anrop:

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
Svarte 25/06/2009 kl. 11:22
kilden bruker

stemmer
7

Nedleggelser i Ruby er en god oversikt for hvordan blokker, lambda og proc arbeid i Ruby, Ruby.

Svarte 28/08/2008 kl. 13:50
kilden bruker

stemmer
6

Forstå Ruby Blokkerer, Procs og lambdaene av Robert Sosinski tydelig forklarer disse programmeringskonsepter og forsterker forklaringene med eksempelkode. Metode gjenstander henger sammen og dekket i tillegg.

Svarte 23/08/2013 kl. 17:07
kilden bruker

stemmer
5

lambda fungerer som forventet, som i andre språk.

Den kablede Proc.newer overraskende og forvirrende.

Den returnsetningen i proc skapt av Proc.newikke bare vil returnere kontrollen bare fra seg selv, men også fra metoden omslutter det .

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

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

Du kan argumentere for at Proc.newsetter kode i omsluttende metoden, akkurat som blokk. Men Proc.newskaper et objekt, mens blokken er en del av et objekt.

Og det er en annen forskjell mellom lambda og Proc.new, som er deres håndtering av (feil) argumenter. lambda klager på det, mens Proc.newignorerer ekstra argumenter eller anser fravær av argumenter som null.

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, proci Ruby 1.8 skaper en lambda, mens i Ruby 1.9+ oppfører seg som Proc.new, som er veldig forvirrende.

Svarte 29/10/2014 kl. 16:22
kilden bruker

stemmer
3

Å utdype Accordion Guy svar:

Legg merke til at Proc.newskaper en proc ut ved å føres en blokk. Jeg tror at lambda {...}analyseres som en slags bokstavelig, snarere enn et metodekall som passerer en blokk. returning fra innsiden av en blokk festet til en metode anrop kommer tilbake fra fremgangsmåten, ikke blokken, og det Proc.newtilfellet er et eksempel på dette på spill.

(Dette er 1,8. Jeg vet ikke hvordan dette omsettes til 1.9.)

Svarte 07/09/2008 kl. 02:31
kilden bruker

stemmer
2

Jeg er litt sent på dette, men det er en stor, men lite kjent ting om Proc.newikke er nevnt i kommentarer i det hele tatt. Som ved dokumentasjon :

Proc::newkan kalles uten en blokk bare innenfor en metode med en tilknyttet blokk, i hvilket tilfelle at blokken blir omdannet tilProc objektet.

Når det er sagt, Proc.newkan til kjedegivende metoder:

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!
Svarte 28/04/2015 kl. 13:15
kilden bruker

stemmer
1

Det er verdt å understreke at returni en proc avkastning fra leksikalsk omslutter metoden, dvs. metoden der proc ble opprettet , ikke metoden som kalles proc. Dette er en konsekvens av nedleggelse eiendom procs. Så følgende kode utganger ingenting:

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

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

foo

Selv om proc utfører i foobar, det ble opprettet i fooog så returnavslutter foo, ikke bare foobar. Som Charles Caldwell skrev ovenfor, har det en GOTO føler for det. Etter min mening, returner fint i en blokk som er utført i sin leksikalske sammenheng, men er mye mindre intuitivt når det brukes i en proc som kjøres i en annen sammenheng.

Svarte 15/11/2018 kl. 10:00
kilden bruker

stemmer
1

Forskjellen i oppførsel med returner IMHO den viktigste forskjellen mellom 2. Jeg foretrekker også lambda fordi det er mindre å skrive enn Proc.new :-)

Svarte 11/08/2008 kl. 02:09
kilden bruker

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