Julia 当我们有这么多结构时,如何定义多个分派?

Julia 当我们有这么多结构时,如何定义多个分派?,julia,Julia,如果我们有一些结构,那么使用多个分派不是问题。 但是当我们有这么多结构时,如何使用多重分派呢? 例如,我们有如下N个结构: struct An a::Float64 end 还有一个函数,比如: f!(a::Ai) = exp(Ai.a) 当N很大时,它会让人头痛。 考虑到这个函数是简单和容易的!一个函数可以这么大 您可以在循环中枚举结构的名称,并使用@eval为每个结构生成和计算代码: julia> for S in [:A1, :A2, :A3] @ev

如果我们有一些结构,那么使用多个分派不是问题。 但是当我们有这么多结构时,如何使用多重分派呢? 例如,我们有如下N个结构:

struct An
   a::Float64
end
还有一个函数,比如:

f!(a::Ai) = exp(Ai.a)
当N很大时,它会让人头痛。
考虑到这个函数是简单和容易的!一个函数可以这么大

您可以在循环中枚举结构的名称,并使用@eval为每个结构生成和计算代码:

julia> for S in [:A1, :A2, :A3]
           @eval begin
               struct $S
                   a::Float64
               end
               f(x::$S) = exp(x.a)
           end
       end

julia> A2(2)
A2(2.0)

julia> f(A2(2))
7.38905609893065
这里我在同一个地方定义了结构,因为我在控制台中进行了尝试


但可能有更好的替代方案。eval通常被认为是次优设计的标志。

您可以在循环中枚举结构的名称,并使用@eval为每个结构生成和计算代码:

julia> for S in [:A1, :A2, :A3]
           @eval begin
               struct $S
                   a::Float64
               end
               f(x::$S) = exp(x.a)
           end
       end

julia> A2(2)
A2(2.0)

julia> f(A2(2))
7.38905609893065
这里我在同一个地方定义了结构,因为我在控制台中进行了尝试


但可能有更好的替代方案。eval通常被认为是次优设计的标志。

如果所有结构的函数定义都相同,则可以将它们定义为某个抽象类型的具体类型,并且只保留一个在抽象类型上分派的函数:

julia> abstract type Allmystructs end

julia> struct A1 <: Allmystructs
           a::Float64
       end

julia> struct A2 <: Allmystructs
           a::Float64
       end
julia> f(A :: Allmystructs) = exp(A.a)
f (generic function with 1 method)

julia> test1 = A1(5)
A1(5.0)

julia> test2 = A2(8)
A2(8.0)

julia> f(test1)
148.4131591025766

julia> f(test2)
2980.9579870417283
当然,如果每种结构类型的函数定义不同,这可能不是您想要的。在这种情况下,元编程可以成为您的朋友


编辑:输入错误。

如果所有结构的函数定义相同,则可以将它们定义为某个抽象类型的具体类型,并只保留一个在抽象类型上分派的函数:

julia> abstract type Allmystructs end

julia> struct A1 <: Allmystructs
           a::Float64
       end

julia> struct A2 <: Allmystructs
           a::Float64
       end
julia> f(A :: Allmystructs) = exp(A.a)
f (generic function with 1 method)

julia> test1 = A1(5)
A1(5.0)

julia> test2 = A2(8)
A2(8.0)

julia> f(test1)
148.4131591025766

julia> f(test2)
2980.9579870417283
当然,如果每种结构类型的函数定义不同,这可能不是您想要的。在这种情况下,元编程可以成为您的朋友


编辑:输入错误。

通常当代码有许多非常相似的结构时,这表明这可能是另一种选择

例如,假设我们有一个带有许多不同结构的彩色几何形状库:

const Point = Tuple{Float64, Float64}

struct Disc
    center::Point
    radius::Float64
    red::Float64
    green::Float64
    blue::Float64
end

struct Rectangle
    topleft::Point
    bottomright::Point
    red::Float64
    green::Float64
    blue::Float64
end

# ... etc., e.g. Triangle, Hexagon
现在假设我们想引入一个亮度函数,它返回形状颜色的感知亮度。一种方法是为每个结构定义一个方法,但由于这些方法都是相同的,我们也可以这样做:

const Shape = Union{Disc, Rectangle, Triangle, Hexagon}
luminance(shape::Shape) = 0.299*shape.red + 0.587*shape.green + 0.114*shape.blue)
这仍然有点烦人,因为我们需要在一个地方列出所有可用的形状。添加新形状会很麻烦。因此,实际上,我们可以制作一个抽象类型的形状结束,并让每个形状子类型结束,正如公认答案中所建议的那样。但在许多方面,这种方法仍然不能令人满意,因为它限制了所有未来形状共享相同的布局

解决此问题的更好方法是解耦所有彩色形状共享的红色、绿色和蓝色属性。因此,我们引入类型层次结构,如下所示:

const Point = Tuple{Float64, Float64}

struct Color
    red::Float64
    green::Float64
    blue::Float64
end

abstract type Figure end

struct Disc <: Figure
    center::Point
    radius::Float64
end

struct Rectangle <: Figure
    topleft::Point
    bottomright::Point
end

struct ColoredShape{F <: Figure}
    figure::F
    color::Color
end
现在,我们不再使用矩形0.0,0.0,1.0,1.0,0.5,0.5,0.5来表示灰色矩形,而是使用彩色ShapeRectange0.0,0.0,1.0,1.0,Color0.5,0.5,0.5。与定义多个相同的亮度方法不同,我们只为颜色结构定义一次。您还可以(可选)为ColoredShape定义另一个委托给属性颜色的方法,但这只是一个附加方法,而不是N!此模式还允许我们为颜色定义的功能在其他上下文(除了彩色形状)中重用


一般来说,最好将概念分解为最小的可消化部分,以实现可重用性和可理解性。如果有很多非常相似的结构,那么为所有结构定义函数似乎是件苦差事,这就意味着可能有一些共享功能需要考虑。

通常当代码有很多非常相似的结构时,这就意味着可能是另一种选择

例如,假设我们有一个带有许多不同结构的彩色几何形状库:

const Point = Tuple{Float64, Float64}

struct Disc
    center::Point
    radius::Float64
    red::Float64
    green::Float64
    blue::Float64
end

struct Rectangle
    topleft::Point
    bottomright::Point
    red::Float64
    green::Float64
    blue::Float64
end

# ... etc., e.g. Triangle, Hexagon
现在假设我们想引入一个亮度函数,它返回形状颜色的感知亮度。一种方法是为每个结构定义一个方法,但由于这些方法都是相同的,我们也可以这样做:

const Shape = Union{Disc, Rectangle, Triangle, Hexagon}
luminance(shape::Shape) = 0.299*shape.red + 0.587*shape.green + 0.114*shape.blue)
这仍然有点烦人,因为我们需要在一个地方列出所有可用的形状。添加新形状会很麻烦。因此,实际上,我们可以制作一个抽象类型的形状结束,并让每个形状子类型结束,正如公认答案中所建议的那样。但在许多方面,这种方法仍然不能令人满意,因为它限制了所有未来形状共享相同的布局

解决此问题的更好方法是解耦所有彩色形状共享的红色、绿色和蓝色属性。因此,我们引入类型层次结构,如下所示:

const Point = Tuple{Float64, Float64}

struct Color
    red::Float64
    green::Float64
    blue::Float64
end

abstract type Figure end

struct Disc <: Figure
    center::Point
    radius::Float64
end

struct Rectangle <: Figure
    topleft::Point
    bottomright::Point
end

struct ColoredShape{F <: Figure}
    figure::F
    color::Color
end
现在,我们不再使用矩形0.0,0.0,1.0,1.0,0.5,0.5,0.5来表示灰色矩形,而是使用彩色ShapeRectange0.0,0.0,1.0,1.0,Color0.5,0.5,0.5。与定义多个相同的亮度方法不同,我们将 d仅为颜色结构定义一次。您还可以(可选)为ColoredShape定义另一个委托给属性颜色的方法,但这只是一个附加方法,而不是N!此模式还允许我们为颜色定义的功能在其他上下文(除了彩色形状)中重用


一般来说,最好将概念分解为最小的可消化部分,以实现可重用性和可理解性。如果有很多非常相似的结构,那么为所有结构定义函数似乎都是件麻烦事,这表明可能有一些共享功能需要考虑。

是否有必要在同一个循环中同时定义函数和结构?不,没有必要。是否有必要在同一个循环中同时定义函数和结构?不,没有必要。这是一个比使用eval更好的解决方案!这是一个很好的解决方案,但应该指出,一般来说,抽象类型应该用于概念相同但结构不同的事物,而不是结构相同的事物。参数化类型或组合可能是更好的选择。这是比使用eval更好的解决方案!这是一个很好的解决方案,但应该指出,一般来说,抽象类型应该用于概念相同但结构不同的事物,而不是结构相同的事物。参数化类型或组合可能是更好的选择。如果你有很多几乎相同的结构,并且做几乎相同的事情,也许你不需要它们全部?为什么有很多非常相似的结构?可以改用参数化类型吗?如果你有大的函数,你能把普通的代码分离出来,而不是为每一种类型重新编写整个代码吗?如果你有很多几乎相同的结构,做几乎相同的事情,也许你不需要它们全部?为什么有很多非常相似的结构?可以改用参数化类型吗?如果你有大型函数,你能把普通代码分离出来,而不是为每种类型重新编写整个代码吗?