2016-03-31 3 views
0

Ich habe ein Array mit Standardzeichenfolgen gefüllt, und ich versuche, einen Teil der Zeichen in der Standardzeichenfolge an einer zufälligen Position zu ersetzen. Array-Element-Änderung verhält sich falsch

Wenn ich so etwas tun, werde ich alle Elemente im Array geändert:

arr = ["*"] * 10 
arr[0][0..2] = "aaa" 
arr 
# => ["aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa"] 

Aber wenn ich ein Array in einer anderen Art und Weise zu initialisieren, es funktioniert:

(0..10).each.map {|i| arr[i] = "*"} 
arr[0][0..2] = "aaa" 
arr 
# => ["aaa", "*", "*", "*", "*", "*", "*", "*", "*", "*", "*"] 

Some mehr initialisieren und alle Elemente gleich:

str = "*" 
(0..10).each.map {|i| arr[i] = str} 
arr[0][0..2] = "aaa" 
arr 
# => ["aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa"] 

Stattdessen habe ich das es mit einzigartigen Elementen zu initialisieren:

str = "*" 
(0..10).each.map {|i| arr[i] = "#{str}" } 
arr[0][0..2] = "aaa" 
arr 
# => ["aaa", "*", "*", "*", "*", "*", "*", "*", "*", "*"] 

Was ist der Hintergrund für ein solches Verhalten?

Antwort

4

Arrays speichern Verweise auf Objekte. Wenn Sie ein Array auf diese Weise initialisieren, erhalten Sie ein Array mit zehn Referenzen auf dieselbe Zeichenfolge. Dann ändern Sie die Zeichenfolge.

arr = ['*']*3 
# => ["*", "*", "*"] 
arr.map &:object_id 
# => [70305424624600, 70305424624600, 70305424624600] 

Für dagegen so Rubin weist eine neue Zeichenfolge für jedes Element:

Array.new(3){ '*' }.map &:object_id 
# => [70184497001120, 70184497001060, 70184497001000] 
+0

ist richtig, weil der eingebaute 'Array # neue' Objektkonstruktor verwendet werden sollte, da er Standardwerte verarbeiten kann. [Lesen Sie die Dokumentation] (http://ruby-doc.org/core-2.2.0/Array.html) – Charles

1

Wenn Sie arr=["*"]*10 tun, setzen Sie das exakt gleiche String Objekt in alle Array-Slots. Dagegen erstellt (0..10).each.map { |i| arr[i] = "*" } ein neues Objekt String für jedes Element im Array.

Illustrated mit dem folgenden Code:

(0..10).each.map { |i| arr[i] = "*" } 
arr[0].equal? arr[1] # Check if first and second elements point to same Object 
# => false 
arr = ["*"] * 10 
arr[0].equal? arr[1] 
# => true 
0
arr = ["*"]*10 

Diese Erklärung eine Reihe von 10 Elementen erstellen, aber alle diese Elemente sind nicht eindeutig und beziehen sich auf die gleiche Instanz der Zeichenfolge "*", die Sie vor dem Füllen Array erstellt haben. Ich meine, dass Ihr Code ist das gleiche wie:

a = "*" 
arr = [a, a, a, a, a, a, a, a, a, a] 
#=> ["*", "*", "*", "*", "*", "*", "*", "*", "*", "*"] 

arr.map(&:object_id) 
#=> [15424420, 15424420, 15424420, 15424420, 15424420, 15424420, 15424420, 15424420, 15424420, 15424420] 

Alle Elemente des Arrays auf den gleichen String-Instanz verweisen, so dass, wenn Sie ein beliebiges Element Wert im Array ändern, dann werden Sie tatsächlich den Wert von ändern eine Variable

["aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa"] 

im zweiten Beispiel, das Sie füllen:, um ein Ergebnis, das Sie das gleiche Array mit eine Variable gefüllt zu bekommen, aber da sein Wert auf „aaa“ geändert wurden dann wird die Ausgabe wie folgt aussehen in jedem elem ent Array mit neuer Instanz von "*" string, wenn Sie also einen einzelnen Array-Wert ändern, betrifft dies nur dieses konkrete Element des Arrays und alle anderen sind gleich, da sie sich auf die verschiedenen zugewiesenen Objekte im Speicher beziehen.

arr = (0..10).each.map { |i| arr[i] = "*" } 
#=> ["*", "*", "*", "*", "*", "*", "*", "*", "*", "*"] 
arr.map(&:object_id) 
#=> [14451520, 14451500, 14451480, 14451440, 14451420, 14451320, 14451300, 14451280, 14451260, 14451240, 14451160] 
+0

* Alle diese Elemente sind nicht einzigartig * - Sie meinen, sie sind einzigartig? – sawa

+0

Ich meine, dass alle Elemente von Array einander durch Wert und Verweis gleich sind, so dass sie nicht eindeutig sind. – SunnyMagadan

Verwandte Themen