Array in disarray


#1

Vi segnalo una curiosità in cui mi sono appena imbattuto e che potrebbe
essere utile sapere.

Iniziamo da questo assegnamento apparentemente innocuo.

2.1.0 :047 > data = [ [] ] * 4
=> [[], [], [], []]
2.1.0 :048 > data[0]
=> []
2.1.0 :052 > data.class
=> Array
2.1.0 :053 > data[0].class
=> Array

Tutto in ordine.

2.1.0 :055 > data[0] << “a”
=> [“a”]
2.1.0 :056 > data
=> [[“a”], [“a”], [“a”], [“a”]]

Ops! Inspettato, vero?

Il problema è solo con i []

2.1.0 :057 > data = [[“a”],[“b”],[“c”],[“d”]]
=> [[“a”], [“b”], [“c”], [“d”]]
2.1.0 :058 > data[0] << “a”
=> [“a”, “a”]
2.1.0 :059 > data
=> [[“a”, “a”], [“b”], [“c”], [“d”]]

Proviamo un’altra via:

2.1.0 :060 > data = [ [] ] * 4
=> [[], [], [], []]
2.1.0 :062 > data[0] = data[0] + [“a”]
=> [“a”]
2.1.0 :063 > data
=> [[“a”], [], [], []]
2.1.0 :064 > data[0] += [“a”]
=> [“a”, “a”]
2.1.0 :065 > data
=> [[“a”, “a”], [], [], []]

Ma la ragione qual è? Nicola R. a cui l’ho mostrato poco fa ha avuto
l’intuizione giusta. Gli [] in data sono lo stesso oggetto:

2.1.0 :067 > seeding_groups[0].object_id # questo è [“a”, “a”]
=> 37696480
2.1.0 :068 > seeding_groups[1].object_id # questi sono i []
=> 37990580
2.1.0 :069 > seeding_groups[2].object_id
=> 37990580
2.1.0 :070 > seeding_groups[3].object_id
=> 37990580

Non lasciatevi ingannare dagli 80 finali, leggete bene tutto il numero.
I quattro [] con cui ho inizializzato data sono in realtà lo stesso
oggetto e assegnare ad uno assegna a tutti.
Invece il metodo + crea un nuovo oggetto.

2.1.0 :071 > x = []
=> []
2.1.0 :072 > x.object_id
=> 37389760 # non è quello di prima, non tutti gli [] sono uguali
2.1.0 :073 > a = [“a”]
=> [“a”]
2.1.0 :074 > a.object_id
=> 37309980
2.1.0 :075 > x += a
=> [“a”]
2.1.0 :076 > x.object_id
=> 37247380

Il colpevole è l’operatore *

2.1.0 :077 > y = [ [“a”] ] * 4
=> [[“a”], [“a”], [“a”], [“a”]]
2.1.0 :078 > y[0].object_id
=> 37037460
2.1.0 :079 > y[1].object_id
=> 37037460

Giustamente non sa come clonare un oggetto generico e sceglie
l’implementazione banale, un po’ inutile a dire il vero.

Il modo corretto per creare il mio array sarebbe stato questo:

2.1.0 :080 > z = Array.new(4) { [] }
=> [[], [], [], []]
2.1.0 :081 > z[0].object_id
=> 36888280
2.1.0 :082 > z[1].object_id
=> 36888240

Ma anche data = [ [], [], [], [] ] sarebbe andato bene!


#2

Ciao Paolo,

Riscrivilo cos e poi chiediti se inaspettato o no :slight_smile:

v = []
data = [ v ] * 4
data[0] << a

oppure v << a"

Saluti,

Chris

Il giorno 04/mar/2014, alle ore 22:07, Paolo M.
removed_email_address@domain.invalid ha scritto:


#3

Lo stesso problema del tipico errore con gli Hash

hsh = Hash.new([])
=> {}

hsh[:key] << 1
=> [1]

hsh[:other]
=> [1]

Dove assegnare un default persistente pi involuto

Hash.new {|h, k| h[k] = [ ] }

(che subdolamente diverso da)

Hash.new { [ ] }

2014-03-04 12:07 GMT+01:00 Paolo M. removed_email_address@domain.invalid:

=> Array
Ops! Inspettato, vero?
Proviamo un’altra via:
=> [[“a”, “a”], [], [], []]
2.1.0 :070 > seeding_groups[3].object_id
=> 37389760 # non quello di prima, non tutti gli [] sono uguali

Il modo corretto per creare il mio array sarebbe stato questo:

Posted via http://www.ruby-forum.com/.


Ml mailing list
removed_email_address@domain.invalid
http://lists.ruby-it.org/mailman/listinfo/ml


twitter: @riffraff
blog (en, it): www.riffraff.info riffraff.blogsome.com
work: circleme.com


#4

Lui non ha usato una variabile per cui il risultato potrebbe essere
inaspettato, dipende dallo scopo del metodo. Io lo uso poco, l’ho fatto
per grosse quantità di dati e non ho mai dovuto referenziare il singolo
elemento.
Ruby ha la tendenza a risparmiare memoria:

irb(main):001:0> a,b=0,0
=> [0, 0]
irb(main):002:0> a.object_id
=> 1
irb(main):003:0> b.object_id
=> 1
irb(main):004:0> a,b=0,1
=> [0, 1]
irb(main):005:0> a.object_id
=> 1
irb(main):006:0> b.object_id
=> 3

nel primo caso ho usato due oggetti diversi ma ruby ha creato una copia.
Questo perchè poi le assegnazioni creeranno una nuova copia per cui
sarebbe uno spreco creare subito oggetti diversi.

Infatti se al posto di interi usassi array gli oggetti sarebbero diversi
da subito:
irb(main):007:0> a,b=[],[]
=> [[], []]
irb(main):008:0> a.object_id
=> 23208504
irb(main):009:0> b.object_id
=> 23208492