Qual è la differenza tra un proc e un lambda in Ruby?


Risposte:


260

Una differenza sta nel modo in cui gestiscono gli argomenti. Creare un proc usando proc {}e Proc.new {}sono equivalenti. Tuttavia, l'utilizzo lambda {}ti dà un proc che controlla il numero di argomenti passati ad esso. Da ri Kernel#lambda:

Equivalente a Proc.new , tranne gli oggetti Proc risultanti controllano il numero di parametri passati quando chiamato.

Un esempio:

p = Proc.new {|a, b| puts a**2+b**2 } # => #<Proc:0x3c7d28@(irb):1>
p.call 1, 2 # => 5
p.call 1 # => NoMethodError: undefined method `**' for nil:NilClass
p.call 1, 2, 3 # => 5
l = lambda {|a, b| puts a**2+b**2 } # => #<Proc:0x15016c@(irb):5 (lambda)>
l.call 1, 2 # => 5
l.call 1 # => ArgumentError: wrong number of arguments (1 for 2)
l.call 1, 2, 3 # => ArgumentError: wrong number of arguments (3 for 2)

Inoltre, come sottolinea Ken, l'uso returnall'interno di un lambda restituisce il valore di quel lambda, ma l'uso returnin un proc ritorna dal blocco che lo racchiude.

lambda { return :foo }.call # => :foo
return # => LocalJumpError: unexpected return
Proc.new { return :foo }.call # => LocalJumpError: unexpected return

Quindi per la maggior parte degli usi rapidi sono gli stessi, ma se si desidera il controllo automatico degli argomenti rigorosi (che a volte può anche aiutare con il debug), o se è necessario utilizzare l' returnistruzione per restituire il valore del proc, utilizzare lambda.


8
Sarebbe corretto dire che i lambda sono molto simili ai metodi (controlla gli argomenti e il ritorno tornerà da loro) mentre i proc sono molto simili ai blocchi (gli argomenti non sono controllati e un ritorno tornerà dal metodo contenente o dal lambda)?
pedz

Sono stato a Dio sa quanti siti Web e articoli ormai non sembrano parlare di utilità di Procs vs. metodi vs. lambdas. Ogni spiegazione fornisce solo un dettaglio spaccato di come i valori di ritorno, ecc., Sono diversi, ma nessuno sul perché è importante. Per ora devo concludere che questo è un casino di design in Ruby.
ankush981

76

La vera differenza tra procs e lambdas ha tutto a che fare con le parole chiave del flusso di controllo. Sto parlando return, raise, break, redo, retryecc - queste parole di controllo. Diciamo che hai una dichiarazione di ritorno in un proc. Quando chiami il tuo proc, non solo ti scaricherà da esso, ma tornerà anche dal metodo allegato, ad esempio:

def my_method
  puts "before proc"
  my_proc = Proc.new do
    puts "inside proc"
    return
  end
  my_proc.call
  puts "after proc"
end

my_method

shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc

Il finale putsdel metodo non è mai stato eseguito, poiché quando abbiamo chiamato il nostro proc, l' returninterno ci ha scaricato dal metodo. Se, tuttavia, convertiamo il nostro proc in un lambda, otteniamo quanto segue:

def my_method
  puts "before proc"
  my_proc = lambda do
    puts "inside proc"
    return
  end
  my_proc.call
  puts "after proc"
end

my_method
shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc
after proc

Il ritorno all'interno della lambda ci scarica solo dalla lambda stessa e il metodo che racchiude continua l'esecuzione. Il modo in cui le parole chiave del flusso di controllo vengono trattate all'interno di procs e lambdas è la principale differenza tra di loro


7

Ci sono solo due differenze principali.

  • Innanzitutto, a lambdaverifica il numero di argomenti passati, mentre a procno. Ciò significa che a lambdagenererà un errore se gli passerai il numero errato di argomenti, mentre a procignorerà gli argomenti imprevisti e lo assegnerà nila quelli mancanti.
  • In secondo luogo, quando lambdaritorna, restituisce il controllo al metodo chiamante; quando procritorna, lo fa immediatamente, senza tornare al metodo di chiamata.

Per vedere come funziona, dai un'occhiata al codice qui sotto. Il nostro primo metodo chiama a proc; il secondo chiama a lambda.

def batman_ironman_proc
  victor = Proc.new { return "Batman will win!" }
  victor.call
  "Iron Man will win!"
end

puts batman_ironman_proc # prints "Batman will win!"

def batman_ironman_lambda
  victor = lambda { return "Batman will win!" }
  victor.call
  "Iron Man will win!"
end

puts batman_ironman_lambda # prints "Iron Man will win!"

Guarda come procdice "Batman vincerà!", Perché ritorna immediatamente, senza tornare al metodo batman_ironman_proc.

Il nostro lambda, tuttavia, torna al metodo dopo essere stato chiamato, quindi il metodo restituisce l'ultimo codice che valuta: "Iron Man vincerà!"


5

# Esempi di Proc

p = Proc.new { |x| puts x*2 }
[1,2,3].each(&p)              # The '&' tells ruby to turn the proc into a block 

proc = Proc.new { puts "Hello World" }
proc.call

# Esempi Lambda

lam = lambda { |x| puts x*2 }
[1,2,3].each(&lam)

lam = lambda { puts "Hello World" }
lam.call           

Differenze tra Procs e Lambdas

Prima di entrare nelle differenze tra procs e lambdas, è importante menzionare che sono entrambi oggetti Proc.

proc = Proc.new { puts "Hello world" }
lam = lambda { puts "Hello World" }

proc.class # returns 'Proc'
lam.class  # returns 'Proc'

Tuttavia, le lambda sono un diverso "sapore" di procs. Questa leggera differenza viene mostrata quando si restituiscono gli oggetti.

proc   # returns '#<Proc:0x007f96b1032d30@(irb):75>'
lam    # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'

1. Lambdas controlla il numero di argomenti, mentre procs no

lam = lambda { |x| puts x }    # creates a lambda that takes 1 argument
lam.call(2)                    # prints out 2
lam.call                       # ArgumentError: wrong number of arguments (0 for 1)
lam.call(1,2,3)                # ArgumentError: wrong number of arguments (3 for 1)

Al contrario, ai proc non importa se viene superato il numero sbagliato di argomenti.

proc = Proc.new { |x| puts x } # creates a proc that takes 1 argument
proc.call(2)                   # prints out 2
proc.call                      # returns nil
proc.call(1,2,3)               # prints out 1 and forgets about the extra arguments

2. Lambdas e procs trattano la parola chiave "return" in modo diverso

'return' all'interno di un lambda attiva il codice appena al di fuori del codice lambda

def lambda_test
  lam = lambda { return }
  lam.call
  puts "Hello world"
end

lambda_test                 # calling lambda_test prints 'Hello World'

'return' all'interno di un proc attiva il codice al di fuori del metodo in cui il proc viene eseguito

def proc_test
  proc = Proc.new { return }
  proc.call
  puts "Hello world"
end

proc_test                 # calling proc_test prints nothing

E per rispondere all'altra tua domanda, quale usare e quando? Seguirò @jtbandes come ha menzionato

Quindi per gli usi più rapidi sono gli stessi, ma se si desidera il controllo automatico degli argomenti rigorosi (che a volte può anche aiutare con il debug), o se è necessario utilizzare l'istruzione return per restituire il valore del proc, usare lambda.

Originariamente pubblicato qui


1

In generale, i lambda sono più intuitivi dei proc perché sono più simili ai metodi. Sono piuttosto severi riguardo all'arità e semplicemente escono quando chiami return. Per questo motivo, molti rubyisti usano la lambda come prima scelta, a meno che non abbiano bisogno delle caratteristiche specifiche dei procs.

Pro: oggetti di classe Proc. Come i blocchi, vengono valutati nell'ambito in cui sono definiti. Lambdas: anche oggetti di classe Procma leggermente diversi dai normali proc. Sono chiusure come blocchi e proc e come tali vengono valutate nell'ambito in cui sono definite.

Creazione di Proc

a = Proc.new { |x| x 2 }

Creare lambda

b = lambda { |x| x 2 }


a = proc { |x| x 2 }è lo stesso dia = Proc.new { |x| x 2 }
lacostenycoder

1

Ecco un altro modo per capirlo.

Un blocco è un blocco di codice associato all'invocazione a una chiamata di un metodo su un oggetto. Nell'esempio seguente, self è un'istanza di una classe anonima che eredita da ActionView :: Base nel framework Rails (che a sua volta include molti moduli di supporto). la carta è un metodo che chiamiamo noi stessi. Passiamo un argomento al metodo e quindi associamo sempre il blocco alla fine dell'invocazione del metodo:

self.card :contacts do |c|
  // a chunk of valid ruby code    
end

Ok, quindi stiamo passando un pezzo di codice a un metodo. Ma come possiamo utilizzare questo blocco? Un'opzione è convertire il blocco di codice in un oggetto. Ruby offre tre modi per convertire un pezzo di codice in un oggetto

# lambda
> l = lambda { |a| a + 1 }
> l.call(1)
=> 2 

# Proc.new
> l2= Proc.new { |a| a + 1 }
> l2.call(1)
=> 2 

# & as the last method argument with a local variable name
def add(&block)
end

Nel metodo sopra, & converte il blocco passato al metodo in un oggetto e memorizza quell'oggetto nel blocco variabile locale. In effetti, possiamo dimostrare che ha lo stesso comportamento di lambda e Proc.new:

def add(&block)
  block
end

l3 = add { |a| a + 1 }
l3.call(1)
=> 2

Questo è importante. Quando si passa un blocco a un metodo e lo si converte utilizzando &, l'oggetto che crea utilizza Proc.new per eseguire la conversione.

Nota che ho evitato l'uso di "proc" come opzione. Questo perché Ruby 1.8, è lo stesso di lambda e in Ruby 1.9, è lo stesso di Proc.new e in tutte le versioni di Ruby dovrebbe essere evitato.

Quindi chiedi qual è la differenza tra lambda e Proc.new?

Innanzitutto, in termini di passaggio dei parametri, lambda si comporta come una chiamata di metodo. Solleverà un'eccezione se si passa un numero errato di argomenti. Al contrario, Proc.new si comporta come un'assegnazione parallela. Tutti gli argomenti inutilizzati vengono convertiti in zero:

> l = lambda {|a,b| puts "#{a} + #{b}" }
 => #<Proc:0x007fbffcb47e40@(irb):19 (lambda)> 
> l.call(1)
ArgumentError: wrong number of arguments (1 for 2)

> l2 = Proc.new {|a,b| puts "#{a} + #{b}" }
=> #<Proc:0x007fbffcb261a0@(irb):21> 
> l2.call(1)
1 + 

In secondo luogo, lambda e Proc.new gestiscono la parola chiave return in modo diverso. Quando si esegue un ritorno all'interno di Proc.new, esso ritorna effettivamente dal metodo che racchiude, ovvero dal contesto circostante. Quando ritorni da un blocco lambda, ritorna semplicemente dal blocco, non dal metodo che lo racchiude. Fondamentalmente, esce dalla chiamata al blocco e continua l'esecuzione con il resto del metodo allegato.

> def add(a,b)
  l = Proc.new { return a + b}
  l.call
  puts "now exiting method"
end
> add(1,1)
=> 2  # NOTICE it never prints the message "now exiting method"

> def add(a,b)
  l = lambda { return a + b }
  l.call
  puts "now exiting method"
end
> add(1,1)
=> now exiting method  # NOTICE this time it prints the message "now exiting method"

Allora perché questa differenza comportamentale? Il motivo è perché con Proc.new possiamo usare iteratori nel contesto dei metodi di chiusura e trarre conclusioni logiche. Guarda questo esempio:

> def print(max)
  [1,2,3,4,5].each do |val|
    puts val
    return if val > max
  end
end
> print(3)
1
2
3
4

Ci aspettiamo che quando invochiamo il ritorno all'interno dell'iteratore, esso ritorni dal metodo allegato. Ricorda che i blocchi passati agli iteratori vengono convertiti in oggetti usando Proc.new ed è per questo che quando usiamo return, uscirà dal metodo che racchiude.

Puoi pensare a lambda come a metodi anonimi, isolano singoli blocchi di codice in un oggetto che può essere trattato come un metodo. In definitiva, pensa a un lambda che si comporta come un metodo anomalo e Proc.new si comporta come un codice inline.


0

Un utile post sulle guide rubino: blocchi, proc e lambda

I proc ritornano dal metodo corrente, mentre i lambda ritornano dalla stessa lambda.

I proc non si preoccupano del numero corretto di argomenti, mentre lambdas solleverà un'eccezione.


-3

la differenza tra proc e lambda è che proc è solo una copia del codice con argomenti a sua volta sostituiti, mentre lambda è una funzione come in altre lingue. (comportamento di ritorno, controllo degli argomenti)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.