2014-12-09 1 views
7

Начальное примечание: Я работаю в Джулии, но этот вопрос, вероятно, относится ко многим языкам.Понимание неизменяемых композитных типов с полями изменяемых типов в Julia

Установка: У меня есть композитный тип следующим образом:

type MyType 
    x::Vector{String} 
end 

Я пишу некоторые методы действуют на MyType. Например, я пишу метод, который позволяет мне вставить новый элемент в x, например. function insert!(d::MyType, itemToInsert::String).

Вопрос: Должно ли MyType быть изменчивым или неизменным?

Мое понимание: Я прочитал Julia docs на этом, а также более общие (и весьма upvoted) вопросы по Stackoverflow (например here или here), но я до сих пор на самом деле не имеют хорошую ручку что значит быть изменяемыми/неизменны с практической точки зрения

Тем не менее, вот моя попытка (особенно для случая неизменного составного типа, содержащего изменяемый массив неизменяемых типов!): Если MyType неизменен, то это означает, что поле x должно всегда указывать на один и тот же объект. Сам этот объект (вектор строк) изменен, поэтому для меня совершенно нормально вставлять в него новые элементы. То, что мне не разрешено, это попробовать и изменить MyType так, чтобы поле x указывало на совершенно другой объект. Например, методы, которые делают следующее в порядке:

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

Но методы, которые делают следующее не в порядке:

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

Правильно ли это? Кроме того, является ли идея, что объект, к которому привязаны значения неизменяемых типов полей, относится к тем, которые создаются внутри конструктора?

Конечная точка: Приносим извинения, если это похоже на дублирование других вопросов. Как было сказано, я просмотрел их и не смог понять, что мне нужно.

ответ

5

Так вот что-то умопомрачительным рассматривать (по крайней мере, для меня):

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 

Так data адрес фактически меняется вектор растет и должна быть перераспределена! Но - вы сами не можете изменять данные, как вы указываете.

Я не уверен, что есть правильный ответ на 100%. В основном я использую immutable для простых типов, таких как пример Complex в документах в некоторых критически важных ситуациях, и я делаю это по причинам «защитного программирования», например. код не нужно писать в поля этого типа, поэтому я делаю это ошибкой. Они являются хорошим выбором ИМО, когда тип является своего рода расширением числа, например. Complex, RGBColor, и я использую их вместо кортежей, как своего рода именованный кортеж (кортежи, похоже, не так хорошо работают с Юлией сейчас, все равно неизменяемые типы выполняются превосходно).

+4

Хотя юлианские массивы не реализованы в самой Джулии, я думаю, что массив имеет несколько полей (для размерности и указателя на память, в которой находятся данные). 'pointer (x.data)' показывает, где это поле памяти указывает ... и что он меняет * внутри * векторного объекта. Но повторите свой эксперимент, на этот раз с помощью ['pointer_from_objref (x.data)'] (http://docs.julialang.org/en/latest/stdlib/base/?highlight=pointer_from_objref#Base.pointer_from_objref), чтобы показать адрес самого объекта Vector. –

+2

См. [Julia.h: 88-120] (https://github.com/JuliaLang/julia/blob/master/src/julia.h#L88-L120) о том, как объекты массива выкладываются в памяти как голова C struct. –

+0

Ах, что имеет смысл – IainDunning