Julia:结构共享属性时如何避免样板代码
Julia: how to avoid boilerplate code when structs sharing attributes
不同的结构(编辑:类型)应该共享一些属性的情况经常发生。如果我作为初学者做对了:在 Julia 中,您可以扩展抽象类型,但它们可能没有任何属性。具体类型(=结构)不可扩展。那么,有没有办法像给定的示例那样避免代码重复(对于属性名称和权重)?
abstract type GameObj end
struct Gem <: GameObj
name::String
weight::Int64
worth::Int64
end
struct Medicine <: GameObj
name::String
weight::Int64
healing_power::Int64
end
g = Gem("diamond", 13, 23000)
m = Medicine("cough syrup", 37, 222)
我尝试将共享属性放入一个额外的结构中,如下例所示。优点:没有代码重复。缺点:调用构造函数和获取属性(g.attributes.weight)不方便
abstract type GameObj end
struct GameObjAttr
name::String
weight::Int64
end
struct Gem <: GameObj
attributes::GameObjAttr
worth::Int64
end
struct Medicine <: GameObj
attritbutes::GameObjAttr
healing_power::Int64
end
g = Gem(GameObjAttr("diamond", 13), 23000)
m = Medicine(GameObjAttr("cough syrup", 37), 222)
第三个例子使用了内部构造函数,现在构造函数的调用更容易阅读和编写,但是现在我们在内部构造函数中有一些代码重复。加:获取共享属性还是不方便:
abstract type GameObj end
struct GameObjAttr
name::String
weight::Int64
end
struct Gem <: GameObj
attributes::GameObjAttr
worth::Int64
Gem(name::String, weight::Int64, worth::Int64) = new(GameObjAttr(name, weight), worth)
end
struct Medicine <: GameObj
attributes::GameObjAttr
healing_power::Int64
Medicine(name::String, weight::Int64, healing_power::Int64) = new(GameObjAttr(name, weight), healing_power)
end
g = Gem("diamond", 13, 23000)
m = Medicine("cough syrup", 37, 222)
有没有其他更好的方法来避免这种代码重复?
(除此之外:是否有必要在内部构造函数中声明类型,或者我们可以保留它吗?)
提前致谢。
你真的需要很多类型的结构来处理一个小的(n < 1000)列表吗?
也许 Julia 的结构主要在处理大量数组时才真正发挥作用
成千上万的相同类型。你是在计划那种平行的大规模,还是只是一个
异构列表?
然而,有一个内置类型用于这种用例,Dict。
GameObject = Dict{String, Any}
g = GameObject("name" => "diamond", "worth" => 23000)
m = GameObject("name" => "medicine", "healing_power" => 222, "worth" => 37)
coin = GameObject("worth" => 1)
这可以很好地工作。随之而来的小烦恼是需要报价
括号中的标签,但可以使用访问函数修复:
# constructor...
newmedicine(worth, healingpower) = GameObject("name" => "medicine",
"worth" => worth, "healing_power" => healingpower)
name(g::GameObject) = try g["name"]; catch; "" end
for o in [g, m, coin]
println(name(o))
end
谢谢!
Perhaps Julia's structs truly come into their own mainly when handling massed arrays of thousands of the same type.
好的,如果我没听错,在面向对象语言中常见的类型层次结构不符合 Julia 的高性能能力。这是有道理的。
我稍微改变了你的代码示例,现在键是符号。
GameObject = Dict{Symbol, Any}
makegem(weight, worth) = GameObject(:name => "gem", :weight => weight, :worth => worth)
makemedicine(weight, healing_power) = GameObject(:name => "medicine", :weight => weight, :healing_power => healing_power)
addweight(o1::GameObject, o2::GameObject) = o1[:weight] + o2[:weight]
g = makegem(13, 23000)
m = makemedicine(37, 222)
addweight(g,m) # = 50
您可以为此使用 Julia 的元编程能力。
abstract type GameObj end
type_fields = Dict(
:Gem => (:worth, Int64),
:Medicine => (:healing_power, Int64)
)
for name in keys(type_fields)
@eval(
struct $name <: GameObj
name::String
weight::Int64
$(type_fields[name][1])::$(type_fields[name][2])
end
)
end
g = Gem("diamond", 13, 23000)
m = Medicine("cough syrup", 37, 222)
这类似于您复制粘贴代码,但它允许您以编程方式执行此操作。请注意,我们使用 $
将外部值插入到循环中正在执行的表达式中。
编辑(根据评论中的问题):
如果您希望能够为不同的类型添加任意数量的字段,您可以对以上代码进行少量修改:
abstract type GameObj end
type_fields = Dict(
:Gem => ((:worth, Int64),
(:something_else, Any)),
:Medicine => ((:healing_power, Int64),)
)
for name in keys(type_fields)
@eval(
struct $name <: GameObj
name::String
weight::Int64
$(map( x -> :($(x[1])::$(x[2])), type_fields[name])...)
end
)
end
g = Gem("diamond", 13, 23000, :hello)
m = Medicine("cough syrup", 37, 222)
不同的结构(编辑:类型)应该共享一些属性的情况经常发生。如果我作为初学者做对了:在 Julia 中,您可以扩展抽象类型,但它们可能没有任何属性。具体类型(=结构)不可扩展。那么,有没有办法像给定的示例那样避免代码重复(对于属性名称和权重)?
abstract type GameObj end
struct Gem <: GameObj
name::String
weight::Int64
worth::Int64
end
struct Medicine <: GameObj
name::String
weight::Int64
healing_power::Int64
end
g = Gem("diamond", 13, 23000)
m = Medicine("cough syrup", 37, 222)
我尝试将共享属性放入一个额外的结构中,如下例所示。优点:没有代码重复。缺点:调用构造函数和获取属性(g.attributes.weight)不方便
abstract type GameObj end
struct GameObjAttr
name::String
weight::Int64
end
struct Gem <: GameObj
attributes::GameObjAttr
worth::Int64
end
struct Medicine <: GameObj
attritbutes::GameObjAttr
healing_power::Int64
end
g = Gem(GameObjAttr("diamond", 13), 23000)
m = Medicine(GameObjAttr("cough syrup", 37), 222)
第三个例子使用了内部构造函数,现在构造函数的调用更容易阅读和编写,但是现在我们在内部构造函数中有一些代码重复。加:获取共享属性还是不方便:
abstract type GameObj end
struct GameObjAttr
name::String
weight::Int64
end
struct Gem <: GameObj
attributes::GameObjAttr
worth::Int64
Gem(name::String, weight::Int64, worth::Int64) = new(GameObjAttr(name, weight), worth)
end
struct Medicine <: GameObj
attributes::GameObjAttr
healing_power::Int64
Medicine(name::String, weight::Int64, healing_power::Int64) = new(GameObjAttr(name, weight), healing_power)
end
g = Gem("diamond", 13, 23000)
m = Medicine("cough syrup", 37, 222)
有没有其他更好的方法来避免这种代码重复? (除此之外:是否有必要在内部构造函数中声明类型,或者我们可以保留它吗?)
提前致谢。
你真的需要很多类型的结构来处理一个小的(n < 1000)列表吗? 也许 Julia 的结构主要在处理大量数组时才真正发挥作用 成千上万的相同类型。你是在计划那种平行的大规模,还是只是一个 异构列表?
然而,有一个内置类型用于这种用例,Dict。
GameObject = Dict{String, Any}
g = GameObject("name" => "diamond", "worth" => 23000)
m = GameObject("name" => "medicine", "healing_power" => 222, "worth" => 37)
coin = GameObject("worth" => 1)
这可以很好地工作。随之而来的小烦恼是需要报价 括号中的标签,但可以使用访问函数修复:
# constructor...
newmedicine(worth, healingpower) = GameObject("name" => "medicine",
"worth" => worth, "healing_power" => healingpower)
name(g::GameObject) = try g["name"]; catch; "" end
for o in [g, m, coin]
println(name(o))
end
谢谢!
Perhaps Julia's structs truly come into their own mainly when handling massed arrays of thousands of the same type.
好的,如果我没听错,在面向对象语言中常见的类型层次结构不符合 Julia 的高性能能力。这是有道理的。
我稍微改变了你的代码示例,现在键是符号。
GameObject = Dict{Symbol, Any}
makegem(weight, worth) = GameObject(:name => "gem", :weight => weight, :worth => worth)
makemedicine(weight, healing_power) = GameObject(:name => "medicine", :weight => weight, :healing_power => healing_power)
addweight(o1::GameObject, o2::GameObject) = o1[:weight] + o2[:weight]
g = makegem(13, 23000)
m = makemedicine(37, 222)
addweight(g,m) # = 50
您可以为此使用 Julia 的元编程能力。
abstract type GameObj end
type_fields = Dict(
:Gem => (:worth, Int64),
:Medicine => (:healing_power, Int64)
)
for name in keys(type_fields)
@eval(
struct $name <: GameObj
name::String
weight::Int64
$(type_fields[name][1])::$(type_fields[name][2])
end
)
end
g = Gem("diamond", 13, 23000)
m = Medicine("cough syrup", 37, 222)
这类似于您复制粘贴代码,但它允许您以编程方式执行此操作。请注意,我们使用 $
将外部值插入到循环中正在执行的表达式中。
编辑(根据评论中的问题):
如果您希望能够为不同的类型添加任意数量的字段,您可以对以上代码进行少量修改:
abstract type GameObj end
type_fields = Dict(
:Gem => ((:worth, Int64),
(:something_else, Any)),
:Medicine => ((:healing_power, Int64),)
)
for name in keys(type_fields)
@eval(
struct $name <: GameObj
name::String
weight::Int64
$(map( x -> :($(x[1])::$(x[2])), type_fields[name])...)
end
)
end
g = Gem("diamond", 13, 23000, :hello)
m = Medicine("cough syrup", 37, 222)