2009-09-29 8 views
2

Je suis au milieu de l'apprentissage Ruby et pensé que j'étais intelligent avec le morceau de code suivant:Modification des variables de niveau du module dans un tableau anonyme en Ruby

[@start,@end].map!{ |time| time += operation == :add ? amount : -(amount) } 

où @Start, @end sont deux niveaux de module variables, l'opération peut être l'une des suivantes: add ou: sub, et amount est une quantité flottante pour ajuster à la fois @start et @end by. En accordant cela me sauve seulement une ligne de code, mais pourquoi cette approche ne fonctionne-t-elle pas, et comment puis-je obtenir quelque chose de similaire?

(Mon résultat attendu est pour @ start/@ end à modifier en conséquence, mais les tests unitaires montrent qu'ils restent à leurs valeurs d'origine.)

Répondre

5

Il est important dans Ruby de se souvenir de la distinction entre les variables et les objets qu'elles contiennent. Le simple fait de définir une variable ne changera jamais l'objet référencé par cette variable. Quand vous faites a += b, c'est juste un raccourci pour a = a + b. Vous assignez donc une nouvelle valeur à la variable a, sans changer l'objet qui était là ou en changeant d'autres références à cet objet. Donc, changer la variable time ne change pas @start.

Pour affecter à une variable d'instance, vous devez réellement affecter à cette variable d'instance. Voici une façon de faire ce que vous recherchez:

operation = :+ 
amount = 12 
@start, @end = [@start, @end].map {|time| time.send(operation, amount)} 

Vous remarquerez que nous ne sommes pas faffing autour de cette activité :add et :sub soit - nous pouvons simplement passer le nom réel du message que nous voulons envoyer (j'ai utilisé + dans ce cas, mais ça pourrait être n'importe quoi).

Si vous aviez une grande liste générée dynamiquement d'ivars que vous vouliez définir, c'est un peu plus compliqué. La seule différence est qu'il faut obtenir et définir les ivars par leur nom.

ivars = [:@start, :@end, :@something_else] 
operation = :+ 
amount = 12 
ivars.each {|ivar| instance_variable_set(ivar, instance_variable_get(ivar).send(operation, amount))} 
+0

Super, merci. Les hypothèses ne s'appliquent tout simplement pas à Ruby. – cfeduke

2

L'opération d'addition dans le bloc ne modifie pas le « temps » , il renvoie une nouvelle valeur. Les éléments du tableau ne sont donc pas modifiés, ils sont remplacés.

+0

Cela a du sens. Je suppose qu'il n'y a aucun moyen de faire fonctionner mon code comme je veux l'écrire. – cfeduke

3

L'opérateur += modifie la valeur de time mais il retourne l'ancienne valeur de time donc le bon code est:

@start,@end = [@start,@end].map!{ |time| time + (operation == :add ? amount : -amount) } 

EDIT Mise à jour le code pour changer réellement @start et @end.

+0

Hmm toujours pas de dés. @start + = opération ==: ajouter? montant: - (montant) fonctionne, mais pas quand j'essaye de le faire à travers le bloc de carte #. :( – cfeduke

+0

Oui, ça fonctionne, merci (je choisis la réponse de Chuck parce qu'il a bien expliqué ce qui se passe sous le capot, et comment je peux penser à propos de Ruby.) – cfeduke

Questions connexes