Wanneer om lambda te gebruik, wanneer om Proc.new te gebruik?
Vra
In Ruby 1.8 is daar subtiele verskille tussen proc/lambda aan die een kant, en Proc.new
op die ander.
- Wat is daardie verskille?
- Kan jy riglyne gee oor hoe om te besluit watter een om te kies?
- In Ruby 1.9 verskil proc en lambda.Wat's die storie?
Oplossing
Nog 'n belangrike maar subtiele verskil tussen procs geskep met lambda
en procs geskep met Proc.new
is hoe hulle die hanteer return
verklaring:
- In 'n
lambda
-geskep proc, diereturn
stelling gee slegs terug vanaf die proses self - In 'n
Proc.new
-geskep proc, diereturn
stelling is 'n bietjie meer verrassend:dit gee beheer nie net van die proses terug nie, maar ook van die metode wat die proc!
Hier is lambda
-proc's geskep return
in aksie.Dit tree op 'n manier wat jy waarskynlik verwag:
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"
Nou hier is 'n Proc.new
-proc's geskep return
dieselfde ding doen.Jy is op die punt om een van daardie gevalle te sien waar Ruby die veelgeroemde beginsel van die minste verrassing verbreek:
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"
Danksy hierdie verrassende gedrag (sowel as minder tik), is ek geneig om te gebruik lambda
oor Proc.new
wanneer pros.
Ander wenke
Om verdere verduideliking te verskaf:
Joey sê dat die terugkeer gedrag van Proc.new
is verrassend.As jy egter in ag neem dat Proc.new soos 'n blok optree, is dit nie verbasend nie, want dit is presies hoe blokke optree.lambas aan die ander kant gedra meer soos metodes.
Dit verklaar eintlik hoekom Procs buigsaam is wanneer dit by arity (aantal argumente) kom, terwyl lambdas dit nie is nie.Blokke vereis nie dat al hul argumente verskaf word nie, maar metodes wel (tensy 'n verstek verskaf word).Alhoewel die verskaffing van lambda-argumentverstek nie 'n opsie in Ruby 1.8 is nie, word dit nou in Ruby 1.9 ondersteun met die alternatiewe lambda-sintaksis (soos opgemerk deur webmat):
concat = ->(a, b=2){ "#{a}#{b}" }
concat.call(4,5) # => "45"
concat.call(1) # => "12"
En Michiel de Mare (die OP) is verkeerd oor die Procs en lambda wat dieselfde met arity optree in Ruby 1.9.Ek het geverifieer dat hulle steeds die gedrag vanaf 1.8 behou soos hierbo gespesifiseer.
break
stellings maak eintlik nie veel sin in Procs of lambdas nie.In Procs sal die pouse jou terugstuur vanaf Proc.new wat reeds voltooi is.En dit maak nie sin om van 'n lambda af te breek nie, aangesien dit in wese 'n metode is, en jy sal nooit van die boonste vlak van 'n metode breek nie.
next
, redo
, en raise
tree dieselfde op in beide Procs en lambdas.Terwyl retry
word ook nie toegelaat nie en sal 'n uitsondering opper.
En uiteindelik, die proc
metode moet nooit gebruik word nie aangesien dit inkonsekwent is en onverwagte gedrag het.In Ruby 1.8 gee dit eintlik 'n lambda terug!In Ruby 1.9 is dit reggestel en dit gee 'n Proc.As jy 'n Proc wil skep, hou by Proc.new
.
Vir meer inligting beveel ek O'Reilly's sterk aan Die Ruby Programmeertaal wat my bron is vir die meeste van hierdie inligting.
ek het gevind hierdie bladsy wat wys wat die verskil tussen Proc.new
en lambda
is.Volgens die bladsy is die enigste verskil dat 'n lambda streng is oor die aantal argumente wat hy aanvaar, terwyl Proc.new
skakel ontbrekende argumente om na nil
.Hier is 'n voorbeeld IRB-sessie wat die verskil illustreer:
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(main):003:0> l.call "hello", "world" => "helloworld" irb(main):004:0> p.call "hello", "world" => "helloworld" irb(main):005:0> l.call "hello" ArgumentError: wrong number of arguments (1 for 2) from (irb):1 from (irb):5:in `call' from (irb):5 from :0 irb(main):006:0> p.call "hello" TypeError: can't convert nil into String from (irb):2:in `+' from (irb):2 from (irb):6:in `call' from (irb):6 from :0
Die bladsy beveel ook aan om lambda te gebruik, tensy jy spesifiek die foutverdraagsame gedrag wil hê.Ek stem saam met hierdie sentiment.Die gebruik van 'n lambda lyk 'n bietjie meer bondig, en met so 'n onbeduidende verskil, blyk dit die beter keuse in die gemiddelde situasie te wees.
Wat Ruby 1.9 betref, jammer, ek het nog nie na 1.9 gekyk nie, maar ek dink nie hulle sal dit so baie verander nie (moet egter nie my woord daarvoor aanvaar nie, dit blyk dat jy van 'n paar veranderinge gehoor het, so Ek is waarskynlik verkeerd daar).
Proc is ouer, maar die semantiek van terugkeer is vir my hoogs teen-intuïtief (ten minste toe ek die taal geleer het) omdat:
- As jy proc gebruik, gebruik jy heel waarskynlik 'n soort funksionele paradigma.
- Proc kan uit die omsluitende omvang terugkeer (sien vorige antwoorde), wat basies 'n goto is, en hoogs nie-funksioneel van aard is.
Lambda is funksioneel veiliger en makliker om oor te redeneer - ek gebruik dit altyd in plaas van proc.
Ek kan nie veel sê oor die subtiele verskille nie.Ek kan egter daarop wys dat Ruby 1.9 nou opsionele parameters vir lambdas en blokke toelaat.
Hier is die nuwe sintaksis vir die stewige lambdas onder 1.9:
stabby = ->(msg='inside the stabby lambda') { puts msg }
Ruby 1.8 het nie daardie sintaksis gehad nie.Die konvensionele manier om blokke/lambda's te verklaar het ook nie opsionele args ondersteun nie:
# 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 ondersteun egter opsionele argumente selfs met die ou sintaksis:
l = lambda { |msg = 'inside the regular lambda'| puts msg }
#=> #<Proc:0x0e5dbc@(irb):1 (lambda)>
l.call
#=> inside the regular lambda
l.call('jeez')
#=> jeez
As jy Ruby1.9 vir Leopard of Linux wil bou, gaan kyk Hierdie artikel (skaamtelose selfbevordering).
Kort antwoord:Wat saak maak is wat return
doen:lambda keer terug uit homself, en proc keer terug uit homself EN die funksie wat dit genoem het.
Wat minder duidelik is, is hoekom jy elkeen wil gebruik.lambda is wat ons verwag dat dinge in 'n funksionele programmeringssin moet doen.Dit is basies 'n anonieme metode met die huidige omvang outomaties gebind.Van die twee is lambda die een wat jy waarskynlik moet gebruik.
Proc, aan die ander kant, is regtig nuttig vir die implementering van die taal self.Jy kan byvoorbeeld "as"-stellings of "vir"-lusse daarmee implementeer.Enige opgawe wat in die proc gevind word, sal terugkeer uit die metode wat dit genoem het, nie die net die "as"-stelling nie.Dit is hoe tale werk, hoe "as"-stellings werk, so my raaiskoot is dat Ruby dit onder die deksels gebruik en hulle het dit net blootgelê omdat dit kragtig gelyk het.
Jy sal dit net regtig nodig hê as jy nuwe taalkonstrukte soos lusse, if-else konstrukte, ens.
'n Goeie manier om dit te sien is dat lambda's in hul eie omvang uitgevoer word (asof dit 'n metode-oproep is), terwyl Procs as inlyn met die oproepmetode uitgevoer kan word, ten minste is dit 'n goeie manier om te besluit watter een om te gebruik in elke geval.
Ek het geen opmerkings oor die derde metode in die queston opgemerk nie, "proc" wat afgekeur is, maar anders hanteer in 1.8 en 1.9.
Hier is 'n taamlik verbose voorbeeld wat dit maklik maak om die verskille tussen die drie soortgelyke oproepe te sien:
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
Sluitings in Ruby is 'n goeie oorsig van hoe blokke, lambda en proc werk in Ruby, met Ruby.
lambda werk soos verwag, soos in ander tale.
Die bedrade Proc.new
is verrassend en verwarrend.
Die return
verklaring in proc geskep deur Proc.new
sal nie net beheer net van homself teruggee nie, maar ook van die metode wat dit omsluit.
def some_method
myproc = Proc.new {return "End."}
myproc.call
# Any code below will not get executed!
# ...
end
Jy kan dit argumenteer Proc.new
voeg kode in die omsluitmetode in, net soos blok.Maar Proc.new
skep 'n voorwerp, terwyl blok is deel van 'n voorwerp.
En daar is nog 'n verskil tussen lambda en Proc.new
, wat hul hantering van (verkeerde) argumente is.lambda kla daaroor, terwyl Proc.new
ignoreer ekstra argumente of beskou die afwesigheid van argumente as nul.
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, proc
in Ruby 1.8 skep 'n lambda, terwyl in Ruby 1.9+ optree soos Proc.new
, wat regtig verwarrend is.
Om uit te brei oor Accordion Guy se reaksie:
Neem waar dat Proc.new
skep 'n proc uit deur 'n blok deurgegee te word.Ek glo dit lambda {...}
word ontleed as 'n soort letterlike, eerder as 'n metode-oproep wat 'n blok verbygaan. return
ing van binne 'n blok geheg aan 'n metode oproep sal terugkeer van die metode, nie die blok, en die Proc.new
geval is 'n voorbeeld hiervan.
(Dit is 1.8.Ek weet nie hoe dit vertaal word na 1.9 nie.)
Ek is bietjie laat hieroor, maar daar is een groot maar min bekende ding oor Proc.new
glad nie in kommentaar genoem nie.Soos deur dokumentasie:
Proc::new
mag sonder 'n blok opgeroep word slegs binne 'n metode met 'n aangehegte blok, in welke geval dat blok word omgeskakel na dieProc
voorwerp.
Dit gesê, Proc.new
laat tot ketting opbrengs metodes:
def m1
yield 'Finally!' if block_given?
end
def m2
m1 &Proc.new
end
m2 { |e| puts e }
#⇒ Finally!
Die verskil in gedrag met return
is IMHO die belangrikste verskil tussen die 2.Ek verkies ook lambda omdat dit minder tik as Proc.new :-)
Dit is die moeite werd om dit te beklemtoon return
in 'n proc terugkeer van die leksikaal omsluitende metode, d.w.s. die metode waar die proc geskep is, nie die metode wat die proc.Dit is 'n gevolg van die sluitingseienskap van procs.Die volgende kode lewer dus niks uit nie:
def foo
proc = Proc.new{return}
foobar(proc)
puts 'foo'
end
def foobar(proc)
proc.call
puts 'foobar'
end
foo
Alhoewel die proses in foobar
, dit is geskep in foo
en so die return
uitgange foo
, nie net foobar
.Soos Charles Caldwell hierbo geskryf het, het dit 'n GOTO-gevoel.Na my mening, return
is goed in 'n blok wat in sy leksikale konteks uitgevoer word, maar is baie minder intuïtief wanneer dit gebruik word in 'n proses wat in 'n ander konteks uitgevoer word.