2015-06-10 6 views
9

Я хотел бы использовать подтип параметра функции в определении моей функции. Это возможно? Например, я хотел бы написать что-то вроде:Могу ли я использовать подтип параметра функции в определении функции?

g{T1, T2<:T1}(x::T1, y::T2) = x + y 

Так что g будет определена для любого x::T1 и любого y, который является подтипом T1. Очевидно, если бы я знал, например, что T1 всегда будет Number, тогда я мог бы написать g{T<:Number}(x::Number, y::T) = x + y, и это будет работать нормально. Но этот вопрос касается случаев, когда T1 неизвестен до времени выполнения.

Читайте дальше, если вам интересно, почему я хотел бы сделать это:

Полное описание того, что я пытаюсь сделать, было бы немного громоздким, но то, что следует, является упрощенным примером.

У меня есть параметризованные типа, и простой метод, определенный в течение этого типа:

type MyVectorType{T} 
    x::Vector{T} 
end 
f1!{T}(m::MyVectorType{T}, xNew::T) = (m.x[1] = xNew) 

У меня также есть другой тип, с абстрактным супер-типа определяется следующим

abstract MyAbstract 
type MyType <: MyAbstract ; end 

я создаю экземпляр MyVectorType с типом векторного элемента, установленным на MyAbstract с использованием:

m1 = MyVectorType(Array(MyAbstract, 1)) 

Теперь я хочу разместить экземпляр MyType в MyVectorType. Я могу это сделать, начиная с MyType <: MyAbstract. Однако я не могу это сделать с f1!, так как определение функции означает, что xNew должно быть типа T, а T будет MyAbstract, а не MyType.

Два решения я могу думать о том, чтобы этой проблемы являются:

f2!(m::MyVectorType, xNew) = (m.x[1] = xNew) 
f3!{T1, T2}(m::MyVectorType{T1}, xNew::T2) = T2 <: T1 ? (m.x[1] = xNew) : error("Oh dear!") 

Первый, по существу, утка-типирование решение. Второй выполняет соответствующую проверку ошибок на первом шаге.

Какой? Или есть третье, лучшее решение, о котором я не знаю?

ответ

11

Способность определить функцию g{T, S<:T}(::Vector{T}, ::S) была указана как «треугольная отправка» в качестве аналогии с диагональю отправки: f{T}(::Vector{T}, ::T). (Представьте таблицу с иерархией типов, обозначающую строки и столбцы, упорядоченные так, чтобы супер-типы были сверху и слева. Строки представляют тип элемента первого аргумента, а столбцы - тип второго. Диагональная отправка будет только соответствуют ячейкам по диагонали стола, тогда как треугольная отправка соответствует диагонали и всему под ним, образуя треугольник.)

Это еще не реализовано . Это сложная проблема, особенно после того, как вы начнете рассматривать область видимости T и S вне определений функций и в контексте инвариантности. См. issue #3766 и #6984 для более подробной информации.

Итак, практически, в этом случае, я думаю, что утиная печать просто прекрасна. Вы полагаетесь на реализацию myVectorType, чтобы выполнить проверку ошибок, когда она назначает свои элементы, что она должна делать в любом случае.

Раствор в базовой Джулии для установки элементов массива-то вроде этого: «треугольника»

f!{T}(A::Vector{T}, x::T) = (A[1] = x) 
f!{T}(A::Vector{T}, x) = f!(A, convert(T, x)) 

Обратите внимание, что он не беспокоится о иерархии типа или подтипа Он просто пытается преобразовать x в T ... который является no-op, если x::S, S<:T. И convert выдает ошибку, если она не может выполнить преобразование или не знает, как это сделать.


UPDATE: Это в настоящее время осуществляется на последней версии развития (0,6-Dev)! В этом случае я думаю, что по-прежнему рекомендую использовать convert, как я первоначально ответил, но теперь вы можете определить ограничения в параметрах статического метода в порядке слева направо.

julia> f!{T1, T2<:T1}(A::Vector{T1}, x::T2) = "success!" 

julia> f!(Any[1,2,3], 4.) 
"success!" 

julia> f!(Integer[1,2,3], 4.) 
ERROR: MethodError: no method matching f!(::Array{Integer,1}, ::Float64) 
Closest candidates are: 
    f!{T1,T2<:T1}(::Array{T1,1}, ::T2<:T1) at REPL[1]:1 

julia> f!([1.,2.,3.], 4.) 
"success!" 
+1

Очень полезный ответ - я многому научился. Большое спасибо. –

Смежные вопросы