E quando useresti uno anziché l'altro?
E quando useresti uno anziché l'altro?
Risposte:
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 return
all'interno di un lambda restituisce il valore di quel lambda, ma l'uso return
in 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' return
istruzione per restituire il valore del proc, utilizzare lambda
.
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
, retry
ecc - 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 puts
del metodo non è mai stato eseguito, poiché quando abbiamo chiamato il nostro proc, l' return
interno 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
Ci sono solo due differenze principali.
lambda
verifica il numero di argomenti passati, mentre a proc
no. Ciò significa che a lambda
genererà un errore se gli passerai il numero errato di argomenti, mentre a proc
ignorerà gli argomenti imprevisti e lo assegnerà nil
a quelli mancanti.lambda
ritorna, restituisce il controllo al metodo chiamante; quando proc
ritorna, 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 proc
dice "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à!"
# 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
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 Proc
ma 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 }
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.
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.
return
dichiarazione restituisceproc
rispetto alambda
.