Forum: Italian Ruby user group Array in disarray

7de465f222e6a9c7fe658e370d0bfe05?d=identicon&s=25 Paolo Montrasio (pmontrasio)
on 2014-03-04 12:07
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 Racco 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!
F855324f298eb7708e39ea117477cbd5?d=identicon&s=25 Chris Corbyn (d11wtq)
on 2014-03-04 12:44
(Received via mailing list)
Ciao Paolo,

Riscrivilo cos e poi chiediti se  inaspettato o no :)

v = []
data = [ v ] * 4
data[0] << a
# oppure v << a"

Saluti,

Chris

Il giorno 04/mar/2014, alle ore 22:07, Paolo Montrasio
<paolo@paolomontrasio.com> ha scritto:
E555a767a33427bfee0bb0878566293c?d=identicon&s=25 gabriele renzi (Guest)
on 2014-03-04 15:02
(Received via mailing list)
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 Montrasio <paolo@paolomontrasio.com>:

>  => 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
> Ml@lists.ruby-it.org
> http://lists.ruby-it.org/mailman/listinfo/ml
>



--
twitter: @riffraff
blog (en, it): www.riffraff.info riffraff.blogsome.com
work: circleme.com
Eff93e9bbe063b7136c9b6f218071a09?d=identicon&s=25 Marco Mastrodonato (marcomd)
on 2014-03-04 15:04
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
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.