Quand utiliser lambda, quand utiliser Proc.new?

voix
316

Dans Ruby 1.8, il existe des différences subtiles entre les proc / lambda d'une part, et Proc.newde l'autre.

  • Quelles sont ces différences?
  • Pouvez-vous donner des directives sur la façon de décider lequel choisir?
  • Dans Ruby 1.9, proc et lambda sont différents. Quel est le problème?
Créé 03/08/2008 à 07:40
source utilisateur
Dans d'autres langues...                            


14 réponses

voix
364

Une autre différence subtile , mais importante entre les procs créés avec lambdaet procs créé avec Proc.newest la façon dont ils gèrent la returndéclaration:

  • Dans un lambda-Création proc, l' returninstruction renvoie uniquement à partir du proc lui - même
  • Dans un Proc.new-Création proc, la returndéclaration est un peu plus surprenant: il revient non seulement le contrôle du proc, mais aussi de la méthode entourant le proc!

Voici lambdace -Création de proc returnen action. Il se comporte d'une manière que vous attendez probablement:

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"

Maintenant , voici une Proc.newde -Création proc returnfaire la même chose. Vous êtes sur le point de voir un de ces cas où Ruby rompt le principe tant vantée de Surprise moins:

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"

Merci à ce comportement surprenant (ainsi que moins de frappe), je tendance à favoriser l' utilisation lambdaplus Proc.newlors de procs.

Créé 03/08/2008 à 16:21
source utilisateur

voix
93

Pour fournir des précisions:

Joey dit que le comportement de retour Proc.newest surprenant. Toutefois , lorsque l' on considère que Proc.new se comporte comme un bloc ce n'est pas surprenant que c'est exactement comment les blocs se comportent. lambas d'autre part se comportent plus comme des méthodes.

Cela explique en fait pourquoi procs sont flexibles en matière de arité (nombre d'arguments), alors que lambdas ne sont pas. Les blocs ne nécessitent pas tous leurs arguments à fournir, mais les méthodes ne (sauf si il est prévu un défaut). Tout en fournissant par défaut de l'argument lambda n'est pas une option dans Ruby 1.8, il est désormais pris en charge dans Ruby 1.9 avec la syntaxe alternative lambda (comme indiqué par WebMAT):

concat = ->(a, b=2){ "#{a}#{b}" }
concat.call(4,5) # => "45"
concat.call(1)   # => "12"

Et Michiel de Mare (OP) est incorrecte sur les procs et lambda se comportent la même chose avec arité dans Ruby 1.9. J'ai vérifié qu'ils maintiennent encore le comportement de 1,8 comme indiqué ci-dessus.

breakdéclarations ne font pas vraiment beaucoup de sens ni dans Procs ou lambdas. En procs, la pause-vous revenir de Proc.new qui a déjà été achevée. Et il ne fait pas de sens de rompre d'un lambda car il est essentiellement une méthode, et vous ne brisera jamais de haut niveau d'une méthode.

next, redoEt raisese comportent de la même dans les deux procs et lambdas. Alors retryn'est pas autorisé dans les deux et soulèvera une exception.

Et enfin, la procméthode ne doit jamais être utilisé comme il est incohérent et a un comportement inattendu. Dans Ruby 1.8 , il retourne en fait un lambda! Dans Ruby 1.9 cela a été corrigé et il renvoie un Proc. Si vous voulez créer un Proc, le bâton avec Proc.new.

Pour plus d' informations, je vous recommande vivement O'Reilly Le Ruby programmation Langue qui est ma source pour la plupart de ces informations.

Créé 04/10/2009 à 06:23
source utilisateur

voix
41

J'ai trouvé cette page qui montre ce que la différence entre Proc.newet lambdasont. Selon la page, la seule différence est qu'un lambda est stricte sur le nombre d'arguments qu'il accepte, alors Proc.newconvertis manquants arguments nil. Voici un exemple de session CISR illustrant la différence:

irb (main): 001: 0> l = lambda {| x, y | x + y}
=> # <Proc: 0x00007fc605ec0748 @ (RIR): 1>
irb (main): 002: 0> p = Proc.new {| x, y | x + y}
=> # <Proc: 0x00007fc605ea8698 @ (RIR): 2>
RIR (principal): 003: 0> l.call "bonjour", "monde"
=> "Helloworld"
RIR (principal): 004: 0> p.call "bonjour", "monde"
=> "Helloworld"
RIR (principal): 005: 0> l.call "bonjour"
ArgumentError: nombre incorrect d'arguments (1 pour 2)
    de (RIR): 1
    de (RIR): 5: dans `appel »
    de (RIR): 5
    à partir de: 0
RIR (principal): 006: 0> p.call "bonjour"
TypeError: ne peut pas convertir nulle en chaîne
    de (RIR): 2: dans `+ »
    de (RIR): 2
    de (RIR): 6: dans `appel »
    de (RIR): 6
    à partir de: 0

La page recommande également d'utiliser lambda, sauf si vous voulez spécifiquement l'erreur comportement tolérant. Je suis d'accord avec ce sentiment. L'utilisation d'un lambda semble un peu plus concis, et avec une telle différence insignifiante, il semble que le meilleur choix dans la situation moyenne.

Quant à Ruby 1.9, désolé, je ne l'ai pas regardé en 1.9, mais je ne pense pas qu'ils changeraient tout ce que beaucoup (ne prenez pas ma parole cependant, il semble que vous ayez entendu parler de quelques changements, donc Je suis sans doute mal là-bas).

Créé 03/08/2008 à 08:28
source utilisateur

voix
14

Proc est plus ancienne, mais la sémantique de retour sont très contre-intuitif pour moi (au moins quand j'apprentissage de la langue) parce que:

  1. Si vous utilisez proc, vous utilisez probablement une sorte de paradigme fonctionnel.
  2. Proc peut revenir sur la portée d'inclusion (voir les réponses précédentes), ce qui est un goto essentiellement, et hautement non fonctionnelle dans la nature.

Lambda est fonctionnellement plus sûr et plus facile à raisonner sur - je l'utilise toujours au lieu de proc.

Créé 11/09/2008 à 00:32
source utilisateur

voix
11

Je ne peux pas dire grand-chose au sujet des différences subtiles. Cependant, je peux souligner que Ruby 1.9 permet désormais des paramètres facultatifs pour les lambdas et les blocs.

Voici la nouvelle syntaxe pour les lambdas Stabby sous 1.9:

stabby = ->(msg='inside the stabby lambda') { puts msg }

Ruby 1.8 n'a pas cette syntaxe. Moi non plus la façon traditionnelle de déclarer des blocs / lambdas soutien args en option:

# 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, cependant, prend en charge les arguments facultatifs même avec l'ancienne syntaxe:

l = lambda { |msg = 'inside the regular lambda'|  puts msg }
#=> #<Proc:0x0e5dbc@(irb):1 (lambda)>
l.call
#=> inside the regular lambda
l.call('jeez')
#=> jeez

Si vous voulez construire ruby1.9 pour Leopard ou Linux, consultez cet article (auto promotion éhontée).

Créé 19/11/2008 à 22:28
source utilisateur

voix
10

Réponse courte: Ce qui compte est ce qui returnfait: le rendement lambda sur lui - même, et revient proc sur elle - même et la fonction qui l'a appelée.

Ce qui est moins clair est la raison pour laquelle vous souhaitez utiliser chacun. lambda est ce que nous attendons les choses devraient faire dans un sens de la programmation fonctionnelle. Il est essentiellement une méthode anonyme avec la portée actuelle automatiquement lié. Des deux, lambda est celui que vous devriez probablement utiliser.

Proc, d'autre part, est vraiment utile pour la mise en œuvre de la langue elle-même. Par exemple, vous pouvez mettre en œuvre « if » ou « pour » boucles avec eux. Tout retour trouvé dans le proc retournera sur la méthode qui l'a appelé, pas simplement la mention « si ». Voici comment langues fonctionnent, comment « si » les déclarations de travail, donc je suppose Ruby utilise sous les couvertures et ils ont juste exposé parce qu'il semblait puissant.

Vous ne vraiment besoin si vous créez de nouvelles constructions de langage comme des boucles, if-else constructions, etc.

Créé 06/10/2011 à 19:33
source utilisateur

voix
9

Une bonne façon de voir est que lambdas sont exécutés dans leur propre champ (comme si elle était un appel de méthode), tandis que Procs peut être considérée comme exécutée en ligne avec la méthode d'appel, au moins c'est une bonne façon de décider Wich un à utiliser dans chaque cas.

Créé 09/12/2008 à 15:17
source utilisateur

voix
8

Je ne l'ai pas remarqué de commentaires sur la troisième méthode dans la queston, « proc » qui est dépréciée, mais traité différemment en 1,8 et 1,9.

Voici un exemple assez bavard qui le rend facile de voir les différences entre les trois appels similaires:

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
Créé 25/06/2009 à 12:22
source utilisateur

voix
7

Ruby dans bouclages est un bon aperçu de la façon dont les blocs, lambda et le travail de proc en Ruby, avec Ruby.

Créé 28/08/2008 à 14:50
source utilisateur

voix
6

Comprendre Ruby blocs, PROC et lambdas par Robert Sosinski explique clairement ces concepts de programmation et renforce les explications avec le code exemple. Méthode objets sont liés et couverts.

Créé 23/08/2013 à 18:07
source utilisateur

voix
5

lambda fonctionne comme prévu, comme dans d'autres langues.

Le câble Proc.newest surprenant et déroutant.

La returndéclaration proc créée par Proc.newnon seulement le contrôle de retour juste de lui - même, mais aussi de la méthode enfermant .

def some_method
  myproc = Proc.new {return "End."}
  myproc.call

  # Any code below will not get executed!
  # ...
end

Vous pouvez faire valoir que Proc.newinsère le code dans la méthode englobante, tout comme bloc. Mais Proc.newcrée un objet, tandis que le bloc est une partie d' un objet.

Et il y a une autre différence entre lambda et Proc.new, ce qui est leur traitement des arguments (de faux). lambda se plaint à ce sujet, tout Proc.newne tient pas compte des arguments supplémentaires ou considère l'absence d'arguments comme nuls.

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, procRuby 1.8 crée un lambda, alors que dans Ruby 1.9+ se comporte comme Proc.new, ce qui est vraiment déroutant.

Créé 29/10/2014 à 16:22
source utilisateur

voix
3

Pour des précisions sur la réponse de l'accordéon Guy:

Notez que Proc.newcrée un proc , en passant à un bloc. Je crois que lambda {...}est analysé comme une sorte de littéral, plutôt que d' un appel de méthode qui passe un bloc. returning à l' intérieur d' un bloc attaché à un appel de méthode retour de la méthode, et non le bloc et le Proc.newcas est un exemple en jeu.

(Ceci est 1.8. Je ne sais pas comment cela se traduit par 1,9.)

Créé 07/09/2008 à 03:31
source utilisateur

voix
2

Je suis un peu en retard sur ce point , mais il y a une grande mais petite chose connue au sujet Proc.newnon mentionné dans les commentaires du tout. Comme par la documentation :

Proc::newpeut être appelé sans un bloc que dans un procédé à un bloc fixé, dans ce cas , ce bloc est converti enProc objet.

Cela dit, Proc.newpermet à des méthodes donnant de la chaîne:

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!
Créé 28/04/2015 à 13:15
source utilisateur

voix
1

La différence de comportement avec returnest à mon humble avis la différence la plus importante entre les 2. Je préfère également lambda parce qu'il est moins taper que Proc.new :-)

Créé 11/08/2008 à 03:09
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more