2015-06-10 129 views
9

我想在我的函数定义中使用函数参数的子类型。这可能吗?例如,我想编写类似:我可以在函数定义中使用函数参数的子类型吗?

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

这样g将任何x::T1进行限定,应当是T1亚型任何y。显然,如果我知道,例如,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)) 

我现在想要在MyVectorType中放置MyType的实例。我可以做到这一点,因为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)。 (想象一下,一个具有标记行和列的类型层次结构的表格,其排列方式使得超类型位于顶部和左侧,行表示第一个参数的元素类型,列表示第二个参数的类型。只匹配沿着桌子对角线的单元格,而三角形调度匹配对角线及其下方的所有元素,形成一个三角形。)

这简直还没有实现 。这是一个复杂的问题,尤其是一旦您开始考虑函数定义之外和不变性环境中的TS的范围。有关更多详细信息,请参阅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 ......如果是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

非常有用的答案 - 我学到了很多。非常感谢。 –

相关问题