Как я могу получить согласованный тип возврата из этой функции? - PullRequest
3 голосов
/ 13 апреля 2020

Есть ли способ получить нижеприведенную функцию для возврата согласованного типа? Я работаю с Джулией GLM (обожаю это). Я написал функцию, которая создает все возможные комбинации регрессии для набора данных. Однако мой текущий метод создания @ формула возвращает различный тип для каждой различной длины rhs.

using GLM

function compose(lhs::Symbol, rhs::AbstractVector{Symbol})
    ts = term.((1, rhs...))
    term(lhs) ~ sum(ts)
end

Использование @code_warntype для простого примера возвращает следующее

julia> @code_warntype compose(:y, [:x])
Variables
  #self#::Core.Compiler.Const(compose, false)
  lhs::Symbol
  rhs::Array{Symbol,1}
  ts::Any

Body::FormulaTerm{Term,_A} where _A
1 ─ %1 = Core.tuple(1)::Core.Compiler.Const((1,), false)
│   %2 = Core._apply(Core.tuple, %1, rhs)::Core.Compiler.PartialStruct(Tuple{Int64,Vararg{Symbol,N} where N}, Any[Core.Compiler.Const(1, false), Vararg{Symbol,N} where N])
│   %3 = Base.broadcasted(Main.term, %2)::Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple},Nothing,typeof(term),_A} where _A<:Tuple
│        (ts = Base.materialize(%3))
│   %5 = Main.term(lhs)::Term
│   %6 = Main.sum(ts)::Any
│   %7 = (%5 ~ %6)::FormulaTerm{Term,_A} where _A
└──      return %7

И проверка типа возврата нескольких различных входных данных:

julia> compose(:y, [:x]) |> typeof
FormulaTerm{Term,Tuple{ConstantTerm{Int64},Term}}

julia> compose(:y, [:x1, :x2]) |> typeof
FormulaTerm{Term,Tuple{ConstantTerm{Int64},Term,Term}}

Мы видим, что при изменении длины rhs меняется тип возвращаемого значения.

Могу ли я изменить свою функцию compose, чтобы она всегда возвращала один и тот же тип? Это не очень большая проблема. Компиляция для каждого нового числа регрессоров занимает всего ~ 70мс. Это действительно больше «как я могу улучшить свои навыки Джулии?»

1 Ответ

3 голосов
/ 13 апреля 2020

Я не думаю, что вы можете избежать нестабильности типа здесь, поскольку ~ ожидает, что RHS будет Term или Tuple из Term с.

Тем не менее, наибольшая стоимость компиляции вы Оплата составляет term.((1, rhs...)), так как вы запускаете вещание, которое компилируется дорого. Вот как вы можете сделать это дешевле:

function compose(lhs::Symbol, rhs::AbstractVector{Symbol})
    term(lhs) ~ ntuple(i -> i <= length(rhs) ? term(rhs[i]) : term(1) , length(rhs)+1)
end

или (это немного медленнее, но больше похоже на ваш оригинальный код):

function compose(lhs::Symbol, rhs::AbstractVector{Symbol})
    term(lhs) ~ map(term, (1, rhs...))
end

Наконец, если вы выполняя такие вычисления, возможно, вы можете отказаться от использования интерфейса формулы, но указать lm или glm непосредственно матрицы как RHS, и в этом случае должна быть возможность избежать дополнительных затрат на компиляцию, например:

julia> y = rand(10);

julia> x = rand(10, 2);

julia> @time lm(x,y);
  0.000048 seconds (18 allocations: 1.688 KiB)

julia> x = rand(10, 3);

julia> @time lm(x,y);
  0.000038 seconds (18 allocations: 2.016 KiB)

julia> y = rand(100);

julia> x = rand(100, 50);

julia> @time lm(x,y);
  0.000263 seconds (22 allocations: 121.172 KiB)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...