Et quand utiliseriez-vous l'un plutôt que l'autre?
Et quand utiliseriez-vous l'un plutôt que l'autre?
Réponses:
Une différence réside dans la manière dont ils traitent les arguments. Créer un proc en utilisant proc {}
et Proc.new {}
sont équivalents. Cependant, utiliser lambda {}
vous donne un proc qui vérifie le nombre d'arguments qui lui sont passés. De ri Kernel#lambda
:
Équivalent à Proc.new , sauf que les objets Proc résultants vérifient le nombre de paramètres transmis lors de l'appel.
Un exemple:
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)
De plus, comme le fait remarquer Ken, utiliser à l' return
intérieur d'un lambda renvoie la valeur de ce lambda, mais utiliser return
dans un proc renvoie à partir du bloc englobant.
lambda { return :foo }.call # => :foo
return # => LocalJumpError: unexpected return
Proc.new { return :foo }.call # => LocalJumpError: unexpected return
Donc, pour la plupart des utilisations rapides, ils sont les mêmes, mais si vous voulez une vérification automatique des arguments stricts (qui peut aussi parfois aider au débogage), ou si vous devez utiliser l' return
instruction pour renvoyer la valeur de proc, utilisez lambda
.
La vraie différence entre procs et lambdas a tout à voir avec les mots-clés de flux de contrôle. Je parle return
, raise
, break
, redo
, retry
etc. - ces mots de contrôle. Disons que vous avez une instruction return dans un proc. Lorsque vous appelez votre proc, il vous en sortira non seulement, mais reviendra également de la méthode englobante, par exemple:
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
Le final puts
de la méthode n'a jamais été exécuté, car lorsque nous avons appelé notre proc, celui- return
ci nous a vidés de la méthode. Si, cependant, nous convertissons notre proc en lambda, nous obtenons ce qui suit:
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
Le retour dans le lambda nous vide uniquement du lambda lui-même et la méthode englobante continue de s'exécuter. La façon dont les mots-clés de flux de contrôle sont traités dans les procs et les lambdas est la principale différence entre eux
Il n'y a que deux différences principales.
lambda
vérifie le nombre d'arguments qui lui sont passés, proc
contrairement à a. Cela signifie que a lambda
lancera une erreur si vous lui transmettez un nombre incorrect d'arguments, tandis que a proc
ignorera les arguments inattendus et les assignera nil
à ceux qui manquent.lambda
retourne, il repasse le contrôle à la méthode appelante; quand a proc
revient, il le fait immédiatement, sans revenir à la méthode appelante.Pour voir comment cela fonctionne, jetez un œil au code ci-dessous. Notre première méthode appelle a proc
; le second appelle 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!"
Voyez comment le proc
dit "Batman va gagner!", C'est parce qu'il revient immédiatement, sans revenir à la méthode batman_ironman_proc.
Notre lambda
, cependant, retourne dans la méthode après avoir été appelée, donc la méthode renvoie le dernier code qu'elle évalue: "Iron Man va gagner!"
# Exemples de processus
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
# Exemples Lambda
lam = lambda { |x| puts x*2 }
[1,2,3].each(&lam)
lam = lambda { puts "Hello World" }
lam.call
Différences entre Procs et Lambdas
Avant d'entrer dans les différences entre procs et lambdas, il est important de mentionner qu'ils sont tous les deux des objets Proc.
proc = Proc.new { puts "Hello world" }
lam = lambda { puts "Hello World" }
proc.class # returns 'Proc'
lam.class # returns 'Proc'
Cependant, les lambdas sont une «saveur» différente des procs. Cette légère différence apparaît lors du retour des objets.
proc # returns '#<Proc:0x007f96b1032d30@(irb):75>'
lam # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'
1. Lambdas vérifie le nombre d'arguments, alors que procs ne le fait pas
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)
En revanche, les procs ne se soucient pas de savoir s'ils reçoivent un nombre incorrect d'arguments.
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 et procs traitent le mot-clé "return" différemment
'return' à l'intérieur d'un lambda déclenche le code juste à l'extérieur du code lambda
def lambda_test
lam = lambda { return }
lam.call
puts "Hello world"
end
lambda_test # calling lambda_test prints 'Hello World'
'return' à l'intérieur d'un proc déclenche le code en dehors de la méthode où le proc est exécuté
def proc_test
proc = Proc.new { return }
proc.call
puts "Hello world"
end
proc_test # calling proc_test prints nothing
Et pour répondre à votre autre question, laquelle utiliser et quand? Je suivrai @jtbandes comme il l'a mentionné
Donc, pour la plupart des utilisations rapides, ils sont les mêmes, mais si vous voulez une vérification automatique des arguments stricts (qui peut aussi parfois aider au débogage), ou si vous devez utiliser l'instruction return pour renvoyer la valeur du proc, utilisez lambda.
Publié à l'origine ici
De manière générale, les lambdas sont plus intuitives que les procs car elles sont plus similaires aux méthodes. Ils sont assez stricts sur l'arité et ils sortent simplement lorsque vous appelez le retour. Pour cette raison, de nombreux Rubyistes utilisent les lambdas comme premier choix, à moins qu'ils n'aient besoin des fonctionnalités spécifiques de procs.
Procs: objets de classe Proc
. Comme les blocs, ils sont évalués dans la portée où ils sont définis.
Lambdas: également des objets de classe Proc
mais subtilement différents des procs normaux. Ce sont des fermetures comme des blocs et des processus, et en tant que telles, elles sont évaluées dans la portée où elles sont définies.
Création de Proc
a = Proc.new { |x| x 2 }
Création de lambda
b = lambda { |x| x 2
}
a = proc { |x| x 2 }
est le même quea = Proc.new { |x| x 2 }
Voici une autre façon de comprendre cela.
Un bloc est un morceau de code attaché à l'appel à un appel d'une méthode sur un objet. Dans l'exemple ci-dessous, self est une instance d'une classe anonyme héritant d'ActionView :: Base dans le framework Rails (qui lui-même comprend de nombreux modules d'assistance). la carte est une méthode que nous appelons nous-mêmes. Nous passons un argument à la méthode puis nous attachons toujours le bloc à la fin de l'appel de la méthode:
self.card :contacts do |c|
// a chunk of valid ruby code
end
Ok, donc nous passons un morceau de code à une méthode. Mais comment utiliser ce bloc? Une option consiste à convertir le morceau de code en un objet. Ruby propose trois façons de convertir un morceau de code en objet
# 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
Dans la méthode ci-dessus, le & convertit le bloc passé à la méthode en un objet et stocke cet objet dans le bloc de variables locales. En fait, nous pouvons montrer qu'il a le même comportement que lambda et Proc.new:
def add(&block)
block
end
l3 = add { |a| a + 1 }
l3.call(1)
=> 2
C'est important. Lorsque vous passez un bloc à une méthode et que vous le convertissez à l'aide de &, l'objet qu'il crée utilise Proc.new pour effectuer la conversion.
Notez que j'ai évité d'utiliser "proc" comme option. C'est parce que c'est Ruby 1.8, c'est la même chose que lambda et dans Ruby 1.9, c'est la même chose que Proc.new et dans toutes les versions de Ruby, cela devrait être évité.
Alors vous demandez quelle est la différence entre lambda et Proc.new?
Premièrement, en termes de passage de paramètres, lambda se comporte comme un appel de méthode. Cela lèvera une exception si vous passez le mauvais nombre d'arguments. En revanche, Proc.new se comporte comme une affectation parallèle. Tous les arguments inutilisés sont convertis en nil:
> 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 +
Deuxièmement, lambda et Proc.new gèrent le mot clé return différemment. Lorsque vous effectuez un retour à l'intérieur de Proc.new, il retourne en fait à partir de la méthode englobante, c'est-à-dire du contexte environnant. Lorsque vous revenez d'un bloc lambda, il revient simplement du bloc, pas de la méthode englobante. Fondamentalement, il quitte l'appel au bloc et continue l'exécution avec le reste de la méthode englobante.
> 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"
Alors pourquoi cette différence de comportement? La raison en est qu'avec Proc.new, nous pouvons utiliser des itérateurs dans le contexte des méthodes englobantes et tirer des conclusions logiques. Regardez cet exemple:
> 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
Nous nous attendons à ce que lorsque nous invoquons return à l'intérieur de l'itérateur, il revienne de la méthode englobante. N'oubliez pas que les blocs passés aux itérateurs sont convertis en objets à l'aide de Proc.new et c'est pourquoi lorsque nous utilisons return, il quittera la méthode englobante.
Vous pouvez considérer les lambdas comme des méthodes anonymes, elles isolent des blocs de code individuels dans un objet qui peut être traité comme une méthode. En fin de compte, pensez qu'un lambda se comporte comme une méthode anomyous et que Proc.new se comporte comme du code en ligne.
Un article utile sur les guides rubis: blocs, procs et lambdas
Les processus retournent à partir de la méthode actuelle, tandis que les lambdas reviennent à partir du lambda lui-même.
Les processus ne se soucient pas du nombre correct d'arguments, tandis que lambdas lèvera une exception.
return
déclaration renvoie de parproc
rapport àlambda
.