Как определить множественную отправку, когда у нас так много структур? - PullRequest
1 голос
/ 09 ноября 2019

Если у нас есть несколько структур, использование множественной отправки не является проблемой. Но когда у нас так много структур, как использовать множественную диспетчеризацию? Например, у нас есть N структур, подобных этому:

struct An
   a::Float64
end

И функция, подобная:

f!(a::Ai) = exp(Ai.a)

Когда N большое, это будет головной болью. Учтите, что эта функция проста и легка! Функция может быть такой большой!

Ответы [ 3 ]

7 голосов
/ 09 ноября 2019

Если определение функции одинаково для всех структур, вы можете определить их как конкретный тип некоторого абстрактного типа и оставить только одну функцию, которая отправляет абстрактный тип:

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

OfКонечно, это может быть не то, что вы ищете, если определение функции для каждого типа структуры отличается. В этом случае метапрограммирование может быть вашим другом.

Редактировать: Опечатки.

3 голосов
/ 09 ноября 2019

Вы можете перечислить имена своих структур в цикле и использовать @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 обычно считается признаком неоптимального дизайна.

0 голосов
/ 15 ноября 2019

Обычно, когда код имеет много очень похожих структур, это говорит о том, что, возможно, композиция является альтернативой.

В качестве примера, предположим, что у нас есть библиотека цветных геометрических фигур с большим количествомразличных структур:

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

Теперь предположим, что мы хотим ввести функцию luminance(), которая возвращает воспринимаемую яркость цвета формы. Одним из способов является определение метода для каждой структуры, но поскольку методы все одинаковые, мы также можем сделать:

const Shape = Union{Disc, Rectangle, Triangle, Hexagon}
luminance(shape::Shape) = 0.299*shape.red + 0.587*shape.green + 0.114*shape.blue)

Это все еще немного раздражает, потому что нам нужно, чтобы все фигуры были доступны водно место, чтобы перечислить их. Добавление новых форм будет хлопот. Таким образом, действительно, мы можем сделать abstract type Shape end и иметь каждый подтип формы, как предложено в принятом ответе. Но во многих отношениях этот подход все еще неудовлетворителен, потому что он ограничивает все будущие Shape s для совместного использования одного и того же макета!

Лучший способ подойти к этой проблеме - отделить red, greenи 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

Теперь вместо использования Rectangle((0.0, 0.0), (1.0, 1.0), 0.5, 0.5, 0.5) для представления серого прямоугольника мы будем использовать ColoredShape(Rectange((0.0, 0.0), (1.0, 1.0)), Color(0.5, 0.5, 0.5)). Вместо определения нескольких идентичных luminance методов, мы бы определили его только один раз для структуры Color. (При желании можно также определить другой метод для ColoredShape, который делегирует свойству color, но это только один дополнительный метод вместо N!). Этот шаблон также позволяет повторно использовать функциональность, которую мы определяем для цветовв других контекстах, кроме цветных форм.

В целом, предпочтительнее разбивать понятия на мельчайшие усваиваемые кусочки для повторного использования и понятности. Если существует множество очень похожих структур, таких, что определение функций для всех из них кажется рутинным делом, это может означать, что может быть какая-то общая функциональность, чтобы выделить ее.

...