Des tableaux dans une classe

Fermé
micro_geek Messages postés 3 Date d'inscription vendredi 18 janvier 2019 Statut Membre Dernière intervention 18 janvier 2019 - 18 janv. 2019 à 05:42
 titour - 19 janv. 2019 à 19:24
Bonjour,
Je commence à programmer en ruby et je suis tombé sur un os.

class ListArray
  attr_accessor :arr
   def initialize arr
      @arr = arr
   end
end

a = ListArray.new [0, 1, 2, 3]

puts a.arr[2]         #=> 2
b = ListArray.new a.arr
a.arr[2] = 999
b.arr[2] = 4

puts a.arr[2]         #=> 4  ???
puts b.arr[2]         #=> 4


a.arr.class donne bien Array. Je devrais donc pouvoir le manipuler comme un tableau.
Pourquoi lorsque je modifie b.arr[2], je modifie aussi a.arr[2] ?
Je ne comprends pas ce que je fais de travers.

Pourriez-vous m'expliquer ?



Configuration: Linux / Firefox 64.0

5 réponses

Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 931
18 janv. 2019 à 08:24
Bonjour
Je ne connais pas Ruby, mais comme les 10 questions précédentes n’ont pas reçues de réponses, il y a fort à parier que la tienne n’en aurait pas eu non plus.

J’emets une hypothèse qui est vraie dans d’autres langages: le passage de paramètres de ton constructeur est fait par référence (éventuellement en pointeur, mais je n’y crois pas, ruby est sensé être simple à coder)

Je commence à programmer en ruby
Ok, mais ça ne dit pas si tu commences à programmer tout court.
Dans tous les cas, si tu avais déjà codé avec des langages où le passage par référence existe soit par défaut (Les VB sauf VB.Net par exemple) soit par défaut selon le type (VB.net, C# par exemple) tu y aurais pensé.

Je vais dinc t’expliquer les passages de paramètres.
D’abord, on va survoler le fonctionnemt des variables et de la mémoire.
Quand tu crées une variable, tu utilises 2 emplacements de mémoire.
Dans l’un est stocké la données, cet emplacement est petit quand il s’agit d’un boolean et très grand quand il s’agit d’un texte de 50000 caractères.
Dans l’autre sont stockés les infos relatives à la variable elle-même, son nom, qui l’a créé, oú se trouve sa donnée etc... Cet emplacement est petit.

Quand on passe un paramètre à une méthode, un constructeur ou simplement que l’on fait a = b, le second emplacement est dupliqué.
Par contre le premier ça dépend.
  • Si c’est un passage par valeur, la donnée est entièrement copiée dans un nouvel emplacement, quand on travaille sur la donnée, celle d’origine n’est pas affectée. C’est gourmand en mémoire et « long » à exécuter.
  • Si c’est un passage par référence, la nouvelle variable pointe sur le même emplacement de mémoire que la première, toute modification se voit forcément depuis les 2 variables puisqu’elles sont associées à la même donnée


A vérifier de ton côté si ruby travaille par référence, parfois , ou pas du tout.
0
micro_geek Messages postés 3 Date d'inscription vendredi 18 janvier 2019 Statut Membre Dernière intervention 18 janvier 2019
18 janv. 2019 à 10:52
Merci beaucoup, Whismeril.

Non, je ne viens pas vraiment d'un autre langage, mais j'en ai parfois survolé certains...
Je comprends ce que tu veux dire : lors de l'appel de la fonction de construction de 'b' j'appelle une fonction de 'a' donc les deux objets pointent au même endroit. ( si j'ai bien saisi )
J'espérais que ce soit quelque chose de simple, mais il semble que ça me dépasse largement.
Je vais tenter de revoir la structure de mon code...
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 931
18 janv. 2019 à 12:35
lors de l'appel de la fonction de construction de 'b' j'appelle une fonction de 'a’
La terminologie n’est pas bonne mais c’est ça.
Lors de l’appel de la méthode de construction de b tu appelles une propriété de a. Là c’est avec les termes « normés » de l’objet. La déclinaison en ruby peut être différente. On voit par exemple Procédure pour une méthode qui ne retourne pas de résultat et Fonction pour celle qui retourne un résultat.


J’ai cherché rapidement sur le net, je n’ai pas trouvé si un tableau est bien passé par référence et comment forcer le passage par valeur. Ça se fait dans les autres langages, y’a pas de raison que ça n’existe pas en ruby.



0
Bonjour.

Un Array est passé par référence, cela se teste en 3 secondes avec l'interpréteur ruby (irb).

diplo@bibi:~$ irb
irb(main):001:0> ar = 1, 2, 3
=> [1, 2, 3]
irb(main):002:0> ar2 = ar
=> [1, 2, 3]
irb(main):003:0> ar.__id__
=> 19279820
irb(main):004:0> ar2.__id__
=> 19279820
irb(main):005:0> ar3 = ar.clone
=> [1, 2, 3]
irb(main):006:0> ar3.__id__
=> 19130860
irb(main):007:0> help

Enter the method name you want to look up.
You can use tab to autocomplete.
Enter a blank line to exit.

>> Array#clone 

= Array#clone

(from ruby core)
=== Implementation from Object
------------------------------------------------------------------------------
  obj.clone -> an_object

------------------------------------------------------------------------------

Produces a shallow copy of obj---the instance variables of obj are
copied, but not the objects they reference. Copies the frozen and tainted
state of obj. See also the discussion under Object#dup.

  class Klass
     attr_accessor :str
  end
  s1 = Klass.new      #=> #<Klass:0x401b3a38>
  s1.str = "Hello"    #=> "Hello"
  s2 = s1.clone       #=> #<Klass:0x401b3998 @str="Hello">
  s2.str[1,4] = "i"   #=> "i"
  s1.inspect          #=> "#<Klass:0x401b3a38 @str=\"Hi\">"
  s2.inspect          #=> "#<Klass:0x401b3998 @str=\"Hi\">"

:


Il faut donc utiliser la méthode clone des objets, mais comme préciser, clone ne copie pas en profondeur les objets, il y a la technique d'utiliser marshal si on a besoin de faire une copie en profondeur, mais pour un Array contenant une liste d'entiers, clone est suffisant.

L'interpréteur ruby facilite l'exécution de petits tests, et fournit une documentation sur les objets en tapant help.
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 931
18 janv. 2019 à 17:21
Et y'a pas moyen de forcer le passage par valeur?
0
titour > Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024
19 janv. 2019 à 18:59
Bonjour.

Pas à ce que je sache.
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 931
19 janv. 2019 à 19:03
OK, dommage.
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
micro_geek Messages postés 3 Date d'inscription vendredi 18 janvier 2019 Statut Membre Dernière intervention 18 janvier 2019
18 janv. 2019 à 19:27
J'ai essayé d'utiliser '.clone', '.dup' et même '.dup.map { |e| e.dup }' mais ça devient rapidement très compliqué et je ne sais plus trop où j'en suis.
Je suppose que ce sont des methodes pour forcer le passage par valeur...

J'ai regardé du coté de la classe Marshal mais je n'ai pas du tout saisi comment m'en servir.

En fait, mon code complet ressemblerai plus à ça :
class OtherClass
   def list
end

class ListArray
   attr_accessor :arr
   def initialize arr
      @arr = arr
   end
   def putItem ...
   def getItem ...
   def cutList &bloc ...
end

a = ListArray.new obj1_other_class.list
  # obj2_other_class.list =>  [[1, 2], [3, 4], ... ]
  # [3, 4] est un item
b = ListArray.new obj2_other_class.list
a.putItem [5, 6]
c = ListArray.new a.arr
c.arr += b.arr
c.arr[1][0] = 7
      ...


Dois-je tout revoir et éviter d'utiliser des tableaux ?
0
Bonjour.

Marshal sert à sérialiser des objets, par ce principe cela produit obligatoirement une copie totale d'un objet.

irb(main):035:0> ar = [[0, 1, 2], [3, 4, 5]]
=> [[0, 1, 2], [3, 4, 5]]
irb(main):036:0> ar2 = ar.clone
=> [[0, 1, 2], [3, 4, 5]]
irb(main):037:0> ar3 = Marshal.load(Marshal.dump(ar))
=> [[0, 1, 2], [3, 4, 5]]
irb(main):038:0> ar[0][0] = 8
=> 8
irb(main):039:0> ar2[0][0]
=> 8
irb(main):040:0> ar3[0][0]
=> 0


Et non, n'abandonne pas les arrays, pour toi, la création d'une méthode retournant la copie en profondeur de ton tableau est suffisante.

Tu aurais même pu le faire avec clone si ton array est composé que d'arrays en utilisant une fonction récursive.

Par ex.

ar = [[1, 2, 3], [[4, 5, 6], [7, 8, 9]], [10, 11, 12]]

def deep_clone array
    ar_clone = []
    array.each{|v| ar_clone << (v.class == Array ? deep_clone(v) : v)}
    ar_clone
end

ar2 = deep_clone ar
ar[1][1][2] = 19
p ar
p ar2
0