2010-12-14 22 views
0

Je suis venu avec cette fonctionnalité étrange (?) de tableaux dans Ruby et il serait très utile si quelqu'un pouvait m'expliquer pourquoi ils fonctionnent comme ils le font.Étrange fonctionnalité? de Ruby Arrays

D'abord, donnons un exemple de la façon dont les choses fonctionnent habituellement.

a = "Hello" #=> "Hello" 
b = a #=> "Hello" 
b += " Goodbye" #=> "Hello Goodbye" 
b #=> "Hello Goodbye" 
a #=> "Hello" 

Ok, quand vous utilisez = il crée une copie de l'objet (cette fois une chaîne de caractères).

Mais lorsque vous utilisez des réseaux cela se produit:

a = [1,2,3] #=> [1,2,3] 
b = a #=> [1,2,3] 
b[1] = 5 #=> [1,5,3] 
b #=> [1,5,3] 
a #=> [1,5,3] 

des thats Maintenant, juste étrange. C'est le seul objet que j'ai trouvé qui ne soit pas copié lors de l'utilisation de = mais qui crée simplement une réfraction à l'objet original.

Est-ce que quelqu'un peut aussi expliquer (il doit y avoir une méthode) pour copier un tableau sans le faire pointer vers l'objet d'origine?

Répondre

11

En fait, vous devriez réexaminer votre établissement.

L'affectation de chaîne est vraiment b = b + " Goodbye". L'opération b + " Goodbye" renvoie une chaîne entièrement nouvelle, donc la variable b pointe vers un nouvel objet après l'affectation. Mais lorsque vous affectez à un élément de tableau individuel, vous ne créez pas un tableau entièrement nouveau, alors a et b continuent de pointer vers le même objet, que vous venez de modifier.

Si vous cherchez une logique pour le comportement mutant vs fonctionnel des tableaux, c'est simple. Il n'y a rien à gagner en modifiant la chaîne. Il est très probablement nécessaire d'allouer de la nouvelle mémoire quand même, donc une chaîne entièrement nouvelle est créée.

Mais un tableau peut être arbitrairement grand. Créer un nouveau tableau pour ne changer qu'un seul élément pourrait être extrêmement coûteux. Et dans tous les cas, un tableau est comme n'importe quel autre objet composite. La modification d'un attribut individuel n'affecte pas nécessairement les autres attributs.

Et pour répondre à votre question, vous pouvez toujours faire:

b = a.dup 
1

Qu'est-ce happends il est que Ruby traite l'objet Array par référence et non par valeur.

Vous pouvez le voir comme ceci:

b= [1,2,3] 
a= b 

       --'b' Points to---> [1,2,3] <--'a' points t--- 

Comme vous pouvez le voir à la fois le point à la même référence, cela signifie que si vous changez quoi que ce soit dans un il sera réfléchi sur b. En ce qui concerne votre question sur la copie de l'objet, vous pouvez utiliser la méthode Object # clone pour ce faire.

+0

.clone, ne semble pas fonctionner pour un tableau à deux démentielle. Je suppose qu'il clone le tableau externe, mais conserve les tableaux internes pointant vers les objets d'origine. Des idées ce que je ferais là? –

+0

.dup a travaillé pour moi. a = [[1,2], [3,4]]; b = a.dup –

1

Essayez votre cas Array avec une chaîne:

a = "Hello" #=> "Hello" 
b = a #=> "Hello" 
b[1] = "x" #=> "x" 
b #=> "Hxllo" 
a #=> "Hxllo" 

chaînes et les tableaux fonctionnent de la même manière à cet égard.

La principale différence dans les deux cas, comme vous les écrit, est la suivante:

b += " Goodbye" 

C'est un raccourci syntaxique pour

b = b + " Goodbye" 

qui crée une nouvelle chaîne de b + " Goodbye" puis assigner que b. La façon de modifier une chaîne existante, plutôt que de créer une nouvelle, est

b << " Goodbye" 

Et si vous branchez que dans votre séquence, vous verrez qu'il modifie a et b, puisque les deux variables font référence au même objet chaîne.

En ce qui concerne la copie profonde, il y a un morceau décent à ce sujet ici:

http://ruby.about.com/od/advancedruby/a/deepcopy.htm