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?
Was dit nuttig?

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, die return stelling gee slegs terug vanaf die proses self
  • In 'n Proc.new-geskep proc, die return 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:

  1. As jy proc gebruik, gebruik jy heel waarskynlik 'n soort funksionele paradigma.
  2. 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. returning 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 die Proc 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.

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top