Dans Ruby 1.8, il existe des différences subtiles entre proc / lambda d'une part et Proc.new
d'autre part.
- 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?
Réponses:
Une autre différence importante mais subtile entre les procs créés avec
lambda
et procs créés avecProc.new
est la façon dont ils gèrent l'return
instruction:lambda
proc -created, l'return
instruction ne retourne que du proc lui-mêmeProc.new
proc -created, l'return
instruction est un peu plus surprenante: elle retourne le contrôle non seulement du proc, mais aussi de la méthode enfermant le proc!Voici les
lambda
procs créésreturn
en action. Il se comporte d'une manière que vous attendez probablement:Maintenant, voici un
Proc.new
proc crééreturn
fait la même chose. Vous êtes sur le point de voir l'un de ces cas où Ruby brise le principe tant vanté de la moindre surprise:Grâce à ce comportement surprenant (ainsi qu'à moins de frappe), j'ai tendance à privilégier l'utilisation de
lambda
overProc.new
lors de la création de procs.la source
proc
méthode. Est-ce juste un raccourci pourProc.new
?proc
équivaut àProc.new
proc
agit commelambda
et non commeProc.new
en ce qui concerne les déclarations de retour. Cela signifie que le document ruby est inexact.proc
agit commelambda
dans 1.8, mais agit commeProc.new
dans 1.9. Voir la réponse de Peter Wagenet.lambda
est une méthode anonyme. Puisqu'il s'agit d'une méthode, elle renvoie une valeur, et la méthode qui l'a appelée peut en faire ce qu'elle veut, y compris l'ignorer et renvoyer une valeur différente. A,Proc
c'est comme coller dans un extrait de code. Cela n'agit pas comme une méthode. Ainsi, lorsqu'un retour se produit dans leProc
, c'est juste une partie du code de la méthode qui l'a appelé.Pour apporter des précisions supplémentaires:
Joey dit que le comportement de retour de
Proc.new
est surprenant. Cependant, lorsque vous considérez que Proc.new se comporte comme un bloc, cela n'est pas surprenant car c'est exactement la façon dont les blocs se comportent. les lambas par contre se comportent plus comme des méthodes.Cela explique en fait pourquoi les Procs sont flexibles en matière d'arité (nombre d'arguments), contrairement aux lambdas. Les blocs ne nécessitent pas que tous leurs arguments soient fournis, contrairement aux méthodes (sauf si une valeur par défaut est fournie). Bien que fournir l'argument lambda par défaut ne soit pas une option dans Ruby 1.8, il est maintenant pris en charge dans Ruby 1.9 avec la syntaxe lambda alternative (comme indiqué par webmat):
Et Michiel de Mare (l'OP) a tort sur les Procs et lambda se comportant de la même manière avec l'arité dans Ruby 1.9. J'ai vérifié qu'ils maintiennent toujours le comportement de 1,8 comme spécifié ci-dessus.
break
les instructions n'ont en fait pas beaucoup de sens dans Procs ou lambdas. Dans Procs, la pause vous renverrait de Proc.new qui a déjà été effectué. Et cela n'a aucun sens de rompre avec un lambda car c'est essentiellement une méthode, et vous ne vous sépareriez jamais du niveau supérieur d'une méthode.next
,redo
Etraise
ont le même comportement dans les deux procs et lambdas. Alors que ceretry
n'est pas autorisé dans les deux cas et entraînera une exception.Et enfin, la
proc
méthode ne doit jamais être utilisée car elle est incohérente 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 souhaitez créer un Proc, restez avecProc.new
.Pour plus d'informations, je recommande fortement le langage de programmation Ruby d' O'Reilly qui est ma source pour la plupart de ces informations.
la source
break
de relances procsLocalJumpError
, alors quebreak
de lambdas de se comporte commereturn
( par exemple ,return nil
).J'ai trouvé cette page qui montre quelle est la différence entre
Proc.new
etlambda
sont. Selon la page, la seule différence est qu'un lambda est strict quant au nombre d'arguments qu'il accepte, alors qu'ilProc.new
convertit les arguments manquants ennil
. Voici un exemple de session IRB illustrant la différence:La page recommande également d'utiliser lambda, sauf si vous souhaitez spécifiquement le comportement tolérant aux erreurs. Je suis d'accord avec ce sentiment. L'utilisation d'un lambda semble un peu plus concise, et avec une différence aussi insignifiante, cela semble être le meilleur choix dans la situation moyenne.
Quant à Ruby 1.9, désolé, je n'ai pas encore examiné 1.9, mais je n'imagine pas qu'ils le changeraient beaucoup (ne me croyez pas sur parole cependant, il semble que vous ayez entendu parler de certains changements, donc Je me trompe probablement là-bas).
la source
Proc est plus ancien, mais la sémantique du retour est très contre-intuitive pour moi (du moins quand j'apprenais la langue) car:
Lambda est fonctionnellement plus sûr et plus facile à raisonner - je l'utilise toujours au lieu de proc.
la source
Je ne peux pas dire grand-chose sur les différences subtiles. Cependant, je peux souligner que Ruby 1.9 autorise désormais des paramètres facultatifs pour les lambdas et les blocs.
Voici la nouvelle syntaxe des lambdas stabby sous 1.9:
Ruby 1.8 n'avait pas cette syntaxe. La manière conventionnelle de déclarer les blocs / lambdas ne prend pas non plus en charge les arguments facultatifs:
Ruby 1.9, cependant, prend en charge les arguments facultatifs même avec l'ancienne syntaxe:
Si vous voulez construire Ruby1.9 pour Leopard ou Linux, consultez cet article (auto promotion sans vergogne).
la source
Réponse courte: ce qui compte, c'est ce qui
return
fait: lambda revient de lui-même, et proc revient de lui-même ET la fonction qui l'a appelé.Ce qui est moins clair, c'est pourquoi vous voulez utiliser chacun d'eux. lambda est ce que nous attendons des choses dans le sens de la programmation fonctionnelle. Il s'agit essentiellement d'une méthode anonyme avec la portée actuelle automatiquement liée. Des deux, lambda est celui que vous devriez probablement utiliser.
Proc, en revanche, est vraiment utile pour implémenter le langage lui-même. Par exemple, vous pouvez implémenter des instructions "if" ou des boucles "for" avec elles. Tout retour trouvé dans le proc sortira de la méthode qui l'a appelé, pas seulement de l'instruction "if". C'est ainsi que les langues fonctionnent, comment les instructions "si" fonctionnent, donc je suppose que Ruby utilise cela sous les couvertures et ils l'ont juste exposé parce qu'il semblait puissant.
Vous n'en aurez vraiment besoin que si vous créez de nouvelles constructions de langage comme des boucles, des constructions if-else, etc.
la source
Une bonne façon de le voir est que les lambdas sont exécutés dans leur propre portée (comme s'il s'agissait d'un appel de méthode), tandis que Procs peut être considéré comme exécuté en ligne avec la méthode appelante, du moins c'est une bonne façon de décider laquelle utiliser. dans chaque cas.
la source
Je n'ai remarqué aucun commentaire sur la troisième méthode dans le queston, "proc" qui est obsolète, mais géré différemment en 1.8 et 1.9.
Voici un exemple assez détaillé qui permet de voir facilement les différences entre les trois appels similaires:
la source
proc
retourné une lambda en 1.8; il a maintenant été corrigé pour retourner un proc en 1.9 - cependant c'est un changement de rupture; donc pas recommandé d'utiliser plusLes fermetures dans Ruby sont un bon aperçu du fonctionnement des blocs, lambda et proc dans Ruby, avec Ruby.
la source
lambda fonctionne comme prévu, comme dans d'autres langues.
Le filaire
Proc.new
est surprenant et déroutant.L'
return
instruction dans proc créée parProc.new
renverra non seulement le contrôle de lui-même, mais aussi de la méthode qui le renferme .Vous pouvez faire valoir que le
Proc.new
code insère dans la méthode englobante, tout comme le bloc. MaisProc.new
crée un objet, tandis que le bloc fait partie d' un objet.Et il y a une autre différence entre lambda et
Proc.new
, qui est leur traitement des (mauvais) arguments. lambda s'en plaint, alors qu'ilProc.new
ignore les arguments supplémentaires ou considère l'absence d'arguments comme nulle.BTW,
proc
dans Ruby 1.8 crée un lambda, tandis que dans Ruby 1.9+ se comporte commeProc.new
, ce qui est vraiment déroutant.la source
Pour développer la réponse de Guy Accordéon:
Notez que
Proc.new
crée un proc out en passant un bloc. Je crois quelambda {...}
c'est analysé comme une sorte de littéral, plutôt qu'un appel de méthode qui passe un bloc.return
ing de l'intérieur d'un bloc attaché à un appel de méthode reviendra de la méthode, pas du bloc, et leProc.new
cas en est un exemple.(Ceci est 1,8. Je ne sais pas comment cela se traduit par 1,9.)
la source
Je suis un peu en retard à ce sujet, mais il y a une grande chose, mais peu connue, qui
Proc.new
n'est pas mentionnée dans les commentaires. Comme par la documentation :Cela dit,
Proc.new
permet d'enchaîner les méthodes de rendement:la source
&block
argument dans ledef
, mais sans avoir à le faire dans la liste def arg.Il vaut la peine de souligner que
return
dans un proc revient de la méthode lexicalement englobante, c'est -à- dire la méthode où le proc a été créé , pas la méthode qui l'a appelé. Ceci est une conséquence de la propriété de fermeture de procs. Ainsi, le code suivant ne produit rien:Bien que le proc s'exécute dans
foobar
, il a été créé dansfoo
et ainsi lesreturn
sortiesfoo
, pas seulementfoobar
. Comme Charles Caldwell l'a écrit ci-dessus, il a une sensation GOTO. À mon avis,return
c'est bien dans un bloc qui est exécuté dans son contexte lexical, mais est beaucoup moins intuitif lorsqu'il est utilisé dans un proc qui est exécuté dans un contexte différent.la source
La différence de comportement avec
return
est à mon humble avis la différence la plus importante entre les 2. Je préfère également lambda parce qu'il est moins typé que Proc.new :-)la source
proc {}
. Je ne sais pas quand cela est entré en vigueur, mais c'est (légèrement) plus facile que d'avoir à taper Proc.new.