Struct Julia结构中的可变域
我无法在stackoverflow和Julia文档中找到以下“设计问题”的答案: 假设我想定义以下对象Struct Julia结构中的可变域,struct,julia,immutability,mutable,Struct,Julia,Immutability,Mutable,我无法在stackoverflow和Julia文档中找到以下“设计问题”的答案: 假设我想定义以下对象 struct Person birthplace::String age::Int end 由于人是不可变的,我很高兴没有人能改变任何人的出生地,尽管如此,这也意味着随着时间的推移,我也不能改变他们的年龄 另一方面,如果我将类型Person定义为 mutable struct Person birthplace::String age::Int end 我现在可以让他们变老了,但我没有以前
struct Person
birthplace::String
age::Int
end
由于人
是不可变的,我很高兴没有人能改变任何人
的出生地,尽管如此,这也意味着随着时间的推移,我也不能改变他们的年龄
另一方面,如果我将类型Person
定义为
mutable struct Person
birthplace::String
age::Int
end
我现在可以让他们变老了
,但我没有以前在出生地
上的安全装置,任何人都可以访问并更改它
到目前为止,我发现的解决方法如下
struct Person
birthplace::String
age::Vector{Int}
end
其中显然age
是一个单元素向量
我发现这个解决方案非常难看,而且肯定是次优的,因为我每次都要用方括号来表示年龄
有没有其他更优雅的方法在对象中同时包含不可变字段和可变字段
也许问题在于我忽略了在结构
中任何东西都是可变或不可变的真正价值。如果是这样的话,你能给我解释一下吗?对于这个特定的例子来说,存储出生日期似乎比存储年龄更好,因为出生日期也是不可变的,根据这些信息计算年龄也很简单,但也许这只是一个玩具例子
我发现这个解决方案非常难看,而且肯定是次优的
每次使用方括号访问年龄
通常,您会定义一个getter,例如age(p::Person)=p.age[1]
,您使用它来代替直接访问字段。这样可以避免括号中的“丑陋”
在这种情况下,如果我们只想存储单个值,也可以使用Ref
(或者可能是0维数组
),类似于:
struct Person
birthplace::String
age::Base.RefValue{Int}
end
Person(b::String, age::Int) = Person(b, Ref(age))
age(p::Person) = p.age[]
使用方法:
julia> p = Person("earth", 20)
Person("earth", 20)
julia> age(p)
20
您已经收到了一些有趣的答案,对于“玩具示例”案例,我喜欢存储出生日期的解决方案。但对于更一般的情况,我可以想出另一种可能有用的方法。将Age
定义为它自己的可变结构,将Person
定义为不可变结构。即:
julia> mutable struct Age ; age::Int ; end
julia> struct Person ; birthplace::String ; age::Age ; end
julia> x = Person("Sydney", Age(10))
Person("Sydney", Age(10))
julia> x.age.age = 11
11
julia> x
Person("Sydney", Age(11))
julia> x.birthplace = "Melbourne"
ERROR: type Person is immutable
julia> x.age = Age(12)
ERROR: type Person is immutable
请注意,我不能更改Person
的任一字段,但我可以通过直接访问可变结构age
中的age
字段来更改年龄。您可以为此定义访问器函数,即:
set_age!(x::Person, newage::Int) = (x.age.age = newage)
julia> set_age!(x, 12)
12
julia> x
Person("Sydney", Age(12))
另一个答案中讨论的向量
解决方案没有问题。因为数组元素是可变的,所以它基本上完成了相同的事情。但是我认为上面的解决方案更简洁。从你表达这个问题的方式来看,我假设你不喜欢一个incrementage
函数来创建一个具有正确年龄的新对象?e、 g.incrementage(p::Person)=Person(p.出生地,p.年龄+1)代码>没错,我不想创建新对象。idea是一个对象,它从web上获取一些信息并更新一些字段。。因为它将每隔10秒左右进行一次轮询,所以每次创建一个新对象并不是我想要的。。不过还是要谢谢你!您可以使用定制的内部构造函数来完全控制什么是可见的,什么是不可见的/可变的,如下所示:(免责声明:无耻地插入自己的问题)很好!我实际上是来自C++,所以我习惯了“类型”的对象。但这绝对不是语言的哲学,因此我不会走这条路。事实上,这是一个玩具的例子。。。一般来说,我倾向于使用“get”函数,就像您所说的,只针对最终用户可以访问的字段,而在代码的深处(用户永远不必查看),我直接访问字段。另外,我不知道直接访问字段和通过函数访问字段之间是否存在性能差异。您可以使用getter仅供内部使用,并且没有性能差异。没有性能差异,因为类型稳定的小函数只会内联函数,因此,编译器将基本上摆脱函数调用并粘贴到字段访问中,使getter成为一个高级零成本抽象。与其使用Array{Int,0}
,不如使用Ref
。至少我上次检查时发现性能有差异。我本来打算提出同样的建议,但最后还是很难看。你可以为Person
创建一个内部构造函数,它采用Int
而不是Age
,这样至少你可以做Person(“墨尔本”,11)
。您还可以使Age
类型可调用,这样您至少可以执行p.Age()
而不是p.Age.Age
。。。但归根结底,它不能无缝地用于Int表达式,除非我们也进行了转换(这可能不值得付出努力)。再说一次,“1的向量”方法也是如此。@TasosPapastylianou是的,同意这一切看起来还是有点混乱,但如果标准是可变和不可变对象的组合,我真的看不到更干净的方法。至少,正如你所说,大多数丑陋可以通过一些直观的访问器功能隐藏起来。如果你的对象有很多字段,你甚至可以通过元编程来避免代码膨胀。我仍然喜欢使用Vector
或Ref
选项,因为我不必定义新的结构。特别是,考虑到我创建struct Age
只是为了让它成为struct Person
中的一个字段,而不在其他任何地方使用它,我觉得这有点愚蠢。目前,由于在我创建的结构中,通常有多个字段是可变的,所以我尝试将它们组合到容器中(通常是Dict
)