Julia struct中的可变字段

ste*_*bat 15 struct mutable immutability julia

我无法在stackoverflow和Julia文档中找到以下"设计问题"的答案:

假设我想定义以下对象

struct Person
birthplace::String
age::Int
end
Run Code Online (Sandbox Code Playgroud)

既然Person是不可改变的,我很高兴没有人可以改变birthplace任何Person创造的,但是,这也意味着当时间过去,我无法改变他们的age......

另一方面,如果我将类型定义Person

mutable struct Person
birthplace::String
age::Int
end
Run Code Online (Sandbox Code Playgroud)

我现在可以制作它们age,但我没有以前的安全性birthplace,任何人都可以访问并更改它.

到目前为止我找到的解决方法如下

struct Person
birthplace::String
age::Vector{Int}
end
Run Code Online (Sandbox Code Playgroud)

显然age是1元素Vector.
我发现这个解决方案非常难看并且绝对不是最理想的,因为每次都必须使用方括号访问年龄.

是否还有其他更优雅的方法在对象中包含不可变字段和可变字段?

也许问题是我错过了在一个内容中具有可变性或不可变性的真正价值struct.如果是这样的话,你能解释一下吗?

fre*_*kre 14

对于这个特殊的例子,最好存储生日,而不是年龄,因为生日也是不可改变的,并且从这些信息中计算年龄很简单,但也许这只是一个玩具的例子.


我发现这个解决方案非常难看并且绝对不是最理想的,因为每次都必须使用方括号访问年龄.

通常你会定义一个getter,即age(p::Person) = p.age[1]你使用的东西,而不是直接访问该字段.这样你就可以避免括号中的"丑陋".

在这种情况下,我们只想存储单个值,也可以使用Ref(或可能是0维Array),例如:

struct Person
    birthplace::String
    age::Base.RefValue{Int}
end
Person(b::String, age::Int) = Person(b, Ref(age))
age(p::Person) = p.age[]
Run Code Online (Sandbox Code Playgroud)

用法:

julia> p = Person("earth", 20)
Person("earth", 20)

julia> age(p)
20
Run Code Online (Sandbox Code Playgroud)

  • 而不是`Array {Int,0}`,最好使用`Ref`.至少在我上次检查时存在性能差异. (3认同)

Col*_*ers 6

你收到了一些有趣的答案,对于"玩具示例"的情况,我喜欢存储出生日期的解决方案.但对于更一般的情况,我可以想到另一种可能有用的方法.定义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
Run Code Online (Sandbox Code Playgroud)

请注意,我不能改变任何一个字段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))
Run Code Online (Sandbox Code Playgroud)

Vector在另一个答案中讨论的解决方案没有任何问题.它基本上完成了同样的事情,因为数组元素是可变的.但我认为上述解决方案更整洁.

  • @Batta 是的,这个真的归结为个人喜好。我个人很高兴为一次性用例创建一个 `struct`。有时这种行为对于类型检查非常有用。例如,在金融领域,您可以将价格和交易量都设为 `Float64` 类型,或者它们可以设为 `Price` 和 `Volume` 类型(只是对 `Float64` 的包装),但是现在如果你的函数需要 `Price`和 `Volume`,你永远不会在你的代码中意外地混淆两者。生产级代码的有用技巧,在这种情况下,像这样的事故会花费很多钱:-) (3认同)

Mar*_*eer 6

在 Julia 1.8 中,您可以使用

mutable struct Person
       age::Int
       const birthplace::String
end
Run Code Online (Sandbox Code Playgroud)

比照。https://docs.julialang.org/en/v1.8-dev/manual/types/#Composite-Types