2014-12-09 7 views
7

Erste Anmerkung: Ich arbeite in Julia, aber diese Frage gilt wahrscheinlich für viele Sprachen.Verstehende unveränderliche zusammengesetzte Typen mit Feldern von veränderbaren Typen in Julia

Setup: Ich habe einen zusammengesetzten Typ wie folgt:

type MyType 
    x::Vector{String} 
end 

Ich schreibe einige Methoden auf MyType zu handeln. Zum Beispiel schreibe ich eine Methode, die es mir erlaubt, ein neues Element in x, z. function insert!(d::MyType, itemToInsert::String).

Frage: Sollte MyType veränderlich oder unveränderlich sein?

Mein Verständnis: ich die Julia docs auf diesem gelesen habe, sowie allgemeinere (und sehr upvoted) Fragen auf Stackoverflow (zB here oder here), aber ich habe immer noch nicht wirklich einen guten Griff auf was es bedeutet, wandelbar/unveränderlich aus praktischer Sicht zu sein

Dennoch, hier ist mein Versuch (vor allem für den Fall eines unveränderlichen zusammengesetzten Typs, einen änderbaren Array von unveränderlichen Typen enthalten!): Wenn MyType unveränderlich ist, dann bedeutet das, dass das Feld x immer auf das gleiche Objekt zeigen muss. Dieses Objekt selbst (ein Vektor von Strings) ist veränderbar, also ist es völlig in Ordnung, neue Elemente darin einzufügen. Was ich nicht tun darf, ist zu versuchen und zu ändern MyType, so dass das Feld x auf ein ganz anderes Objekt zeigt. Zum Beispiel sind Verfahren, die die folgenden sind in Ordnung zu tun:

MyType.x[1] = "NewValue" 
push!(MyType.x, "NewElementToAdd") 

Aber Methoden, die die folgenden sind nicht in Ordnung zu tun:

MyType.x = ["a", "different", "string", "array"] 

Ist das richtig? Ist auch die Idee, dass das Objekt, mit dem ein unveränderlicher Typenfeld-Werte gesperrt sind, diejenigen sind, die innerhalb des Konstruktors erstellt werden?

Endgültiger Punkt: Ich entschuldige mich, wenn dies scheint, andere Fragen zu SO zu duplizieren. Wie gesagt, ich habe sie durchgesehen und konnte nicht verstehen, was ich suchte.

Antwort

5

Also hier biegt etwas Geist (zumindest für mich) zu berücksichtigen:

julia> immutable Foo 
     data::Vector{Float64} 
     end 

julia> x = Foo([1.0, 2.0, 4.0]) 
Foo([1.0,2.0,4.0]) 

julia> append!(x.data, x.data); pointer(x.data) 
Ptr{Float64} @0x00007ffbc3332018 

julia> append!(x.data, x.data); pointer(x.data) 
Ptr{Float64} @0x00007ffbc296ac28 

julia> append!(x.data, x.data); pointer(x.data) 
Ptr{Float64} @0x00007ffbc34809d8 

So ist die data Adresse ändert sich tatsächlich als der Vektor wächst und muss umverteilt werden! Aber - Sie können die Daten nicht selbst ändern, wie Sie darauf hinweisen.

Ich bin mir nicht sicher, es gibt eine 100% richtige Antwort ist wirklich. Ich verwende hauptsächlich immutable für einfache Typen wie das Complex Beispiel in den Dokumenten in einigen leistungskritischen Situationen, und ich mache es für "defensive Programmierung" Gründen, z. Der Code muss nicht in die Felder dieses Typs schreiben, deshalb mache ich einen Fehler damit. Sie sind IMO eine gute Wahl, wenn der Typ eine Art einer Erweiterung einer Nummer ist, z. Complex, RGBColor, und ich benutze sie anstelle von Tupeln, als eine Art benanntes Tupel (Tupel scheinen sowieso nicht gut mit Julia zu funktionieren, wo unveränderliche Typen hervorragend funktionieren).

+4

Während Julian Arrays nicht in Julia selbst implementiert sind, denke ich, dass ein Array mehrere Felder hat (für die Dimensionalität und den Zeiger auf den Speicher, in dem sich die Daten befinden). 'pointer (x.data)' zeigt an, wo dieses Speicherfeld zeigt ... und dass es * sich innerhalb * des Vektorobjekts ändert. Aber wiederholen Sie Ihr Experiment, diesmal mit ['pointer_from_objref (x.data)'] (http://docs.julaulang.org/en/latest/stdlib/base/?highlight=pointer_from_objref#Base.pointer_from_objref), um die Adresse anzuzeigen des Vector-Objekts selbst. –

+2

Siehe [julia.h: 88-120] (https://github.com/JuliaLang/julia/blob/master/src/julia.h#L88-L120) für die Anordnung der Array-Objekte im Speicher als Nackte C-Struktur. –

+0

Ah, das macht mehr Sinn – IainDunning