Julia 结构中的可变字段
mutable fields in Julia struct
我无法在 Whosebug 和 Julia 文档中找到以下 "design problem":
的答案
假设我想定义以下对象
struct Person
birthplace::String
age::Int
end
既然Person
是不可变的,我很高兴没有人可以改变任何Person
的birthplace
创造,尽管如此,这也意味着当时间流逝时,我无法改变他们 age
或者...
另一方面,如果我将类型 Person
定义为
mutable struct Person
birthplace::String
age::Int
end
我现在可以制作它们 age
,但我没有以前在 birthplace
上拥有的安全性,任何人都可以访问并更改它。
目前我找到的解决方法如下
struct Person
birthplace::String
age::Vector{Int}
end
显然 age
是一个 1 元素 Vector
。
我发现这个解决方案非常难看,而且绝对不是最优的,因为我每次都必须使用方括号访问年龄。
有没有其他更优雅的方法可以在对象中同时拥有不可变和可变字段?
也许问题是我错过了在 struct
中让所有内容可变或不可变的真正价值。如果是这样的话,你能给我解释一下吗?
对于这个特定的例子,存储生日而不是年龄似乎更好,因为生日也是不可变的,而且根据该信息计算年龄也很简单,但也许这只是一个玩具示例。
I find this solution quite ugly and definitely suboptimal as I have to
access the age with the square brackets every time.
通常您会定义一个 getter,即您使用的 age(p::Person) = p.age[1]
之类的东西,而不是直接访问该字段。这样你就可以避免 "ugliness" 和方括号。
在这种情况下,我们只想存储单个值,也可以使用 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[]
使用情况:
julia> p = Person("earth", 20)
Person("earth", 20)
julia> age(p)
20
您收到了一些有趣的答案,对于 "toy example" 案例,我喜欢存储出生日期的解决方案。但对于更一般的情况,我可以想到另一种可能有用的方法。将 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))
另一个答案中讨论的Vector
解决方案没有任何问题。它本质上是在完成同样的事情,因为数组元素是可变的。但我认为上面的解决方案更简洁。
在 Julia 1.8 中,您可以使用
mutable struct Person
age::Int
const birthplace::String
end
比照。 https://docs.julialang.org/en/v1.8-dev/manual/types/#Composite-Types
我无法在 Whosebug 和 Julia 文档中找到以下 "design problem":
的答案假设我想定义以下对象
struct Person
birthplace::String
age::Int
end
既然Person
是不可变的,我很高兴没有人可以改变任何Person
的birthplace
创造,尽管如此,这也意味着当时间流逝时,我无法改变他们 age
或者...
另一方面,如果我将类型 Person
定义为
mutable struct Person
birthplace::String
age::Int
end
我现在可以制作它们 age
,但我没有以前在 birthplace
上拥有的安全性,任何人都可以访问并更改它。
目前我找到的解决方法如下
struct Person
birthplace::String
age::Vector{Int}
end
显然 age
是一个 1 元素 Vector
。
我发现这个解决方案非常难看,而且绝对不是最优的,因为我每次都必须使用方括号访问年龄。
有没有其他更优雅的方法可以在对象中同时拥有不可变和可变字段?
也许问题是我错过了在 struct
中让所有内容可变或不可变的真正价值。如果是这样的话,你能给我解释一下吗?
对于这个特定的例子,存储生日而不是年龄似乎更好,因为生日也是不可变的,而且根据该信息计算年龄也很简单,但也许这只是一个玩具示例。
I find this solution quite ugly and definitely suboptimal as I have to access the age with the square brackets every time.
通常您会定义一个 getter,即您使用的 age(p::Person) = p.age[1]
之类的东西,而不是直接访问该字段。这样你就可以避免 "ugliness" 和方括号。
在这种情况下,我们只想存储单个值,也可以使用 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[]
使用情况:
julia> p = Person("earth", 20)
Person("earth", 20)
julia> age(p)
20
您收到了一些有趣的答案,对于 "toy example" 案例,我喜欢存储出生日期的解决方案。但对于更一般的情况,我可以想到另一种可能有用的方法。将 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))
另一个答案中讨论的Vector
解决方案没有任何问题。它本质上是在完成同样的事情,因为数组元素是可变的。但我认为上面的解决方案更简洁。
在 Julia 1.8 中,您可以使用
mutable struct Person
age::Int
const birthplace::String
end
比照。 https://docs.julialang.org/en/v1.8-dev/manual/types/#Composite-Types