Julia:为什么在函数中指定参数类型会导致(看似)不一致的行为?
Julia: Why does specifying the types of arguments in functions lead to (seemingly) inconsistent behaviour?
我有一个函数接受 7 个(关键字)参数,每个参数都指定了类型,最后一个参数有默认值,如下所示:
function dummy(;truefalse1::S, somevar1::T, somevar2::T, somevar3::T, somevar4::T,
scalarvar::Int64, truefalse2::D = falses(3, 3)) where {
T <: Union{Array{Float64,2}, SubArray{Float64, 2}},
S <: AbstractArray{Bool}, D <: AbstractArray{Bool}}
###
end
truefalse*
参数可以是二维布尔数组 (BitArray{2}) 或它的视图(例如 view(somearray, 2:4, 3:5)
)。 somevar*
参数可以是 Float64 类型的二维数组或此类数组的“view
”。
以上有效,但这个看似等效的版本无效(测试输入见下文):
function dummy(;truefalse1::S, somevar1::T, somevar2::T, somevar3::T, somevar4::T,
scalarvar::Int64, truefalse2::S = falses(3, 3)) where {
T <: Union{Array{Float64,2}, SubArray{Float64, 2}},
S <: AbstractArray{Bool}}
###
end
(换句话说,D
类型已被删除,两次出现都使用 S
类型。)
报错信息如下:
ERROR: MethodError: no method matching #dummy#823(::SubArray{Bool,2,BitArray{2},Tuple{UnitRange{Int64},UnitRange{Int64}},false}, ::SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false}, ::SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false}, ::SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false}, ::SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false}, ::Int64, ::BitArray{2}, ::typeof(wheretonext))
Closest candidates are:
#dummy#823(::S, ::T, ::T, ::T, ::T, ::Int64, ::S, ::typeof(dummy)) where {T<:Union{Array{Float64,2}, SubArray{Float64,2,P,I,L} where L where I where P}, S<:(AbstractArray{Bool,N} where N)} at /SomePath/someDummyCode.jl:238
Stacktrace:
[1] (::var"#kw##dummy")(::NamedTuple{(:truefalse1, :somevar1, :somevar2, :somevar3, :somevar4, :scalarvar),Tuple{SubArray{Bool,2,BitArray{2},Tuple{UnitRange{Int64},UnitRange{Int64}},false},SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false},SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false},SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false},SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false},Int64}}, ::typeof(dummy)) at ./none:0
[2] top-level scope at none:0
这是测试输入的样本集:
julia> using Random; Random.seed!(1234);
julia> trf1 = rand(5, 10) .> rand(5, 10); trf2 = rand(5, 10) .> rand(5, 10);
julia> smv1 = rand(5, 10); smv2 = rand(5, 10); smv3 = rand(5, 10); smv4 = rand(5, 10);
这些情况使用第二个函数声明产生上述错误:
julia> dummy(truefalse1 = view(trf1, 2:4, 3:5), somevar1 = view(smv1, 2:4, 3:5),
somevar2 = view(smv2, 2:4, 3:5), somevar3 = view(smv3, 2:4, 3:5),
somevar4 = view(smv4, 2:4, 3:5), scalarvar = 1)
julia> dummy(truefalse1 = view(trf1, 2:4, 3:5), somevar1 = view(smv1, 2:4, 3:5),
somevar2 = view(smv2, 2:4, 3:5), somevar3 = view(smv3, 2:4, 3:5),
somevar4 = view(smv4, 2:4, 3:5), scalarvar = 1, truefalse2 = falses(3, 3))
但是,仍然使用第二个函数声明,这种情况下工作正常:
julia> dummy(truefalse1 = view(trf1, 2:4, 3:5), somevar1 = view(smv1, 2:4, 3:5),
somevar2 = view(smv2, 2:4, 3:5), somevar3 = view(smv3, 2:4, 3:5),
somevar4 = view(smv4, 2:4, 3:5), scalarvar = 1, truefalse2 = view(trf2, 2:4, 3:5))
(回想一下:以上所有测试用例都可以在第一个函数声明中正常工作。)
对于我可能做错了什么或 Julia 语言中的相关微妙之处的任何建议,我将不胜感激。这是我在 Julia 中编写代码的第二周,所以我也欢迎任何其他提示。谢谢!
这是因为在第一个定义中,你让truefalse1
和truefalse2
有不同的类型S
和D
,这两种类型都是[=16=的子类型].而在第二个定义中,truefalse1
和 truefalse2
必须具有相同的类型 S
(约束条件是 S
是 AbstractArray{Bool}
的子类型)。
Parametric Methods 的文档应该对此进行更详细的解释,但也许以下更简单的示例可以帮助您了解其工作原理:
# a and b can be of different types
function foo(a::S, b::T) where {
S<:AbstractArray{Bool},
T<:AbstractArray{Bool}}
end
# a and b must have the same type
function bar(a::S, b::S) where {
S<:AbstractArray{Bool}}
end
# test data
a = rand(Bool, 10); # Array
b = rand(Bool, 10); # Array
c = view(b, 1:5); # SubArray
以下调用均有效:
# OK because:
# - typeof(a) == Array{Bool,1} <: AbstractArray{Bool}
# - typeof(b) == Array{Bool,1} <: AbstractArray{Bool}
# => substitute S for Array{Bool,1} and T for Array{Bool,1}
julia> foo(a, b)
# OK because:
# typeof(a) == typeof(b) == Array{Bool,1} <: AbstractArray{Bool}
# => substitute S for Array{Bool,1}
julia> bar(a, b)
# OK because:
# - a isa Array{Bool,1} <: AbstractArray
# - c isa SubArray{Bool,...} <: AbstractArray{Bool}
# => substitute S for Array{Bool,1} and T for SubArray{Bool,...}
julia> foo(a, c)
# Not OK because typeof(a) != typeof(b)
# there is no concrete type S such that
# - a isa S
# - b isa S
# - S <: AbstractArray
# => Method does not match
julia> bar(a, c)
ERROR: MethodError: no method matching bar(::Array{Bool,1}, ::SubArray{Bool,1,Array
{Bool,1},Tuple{UnitRange{Int64}},true})
Closest candidates are:
bar(::S, ::S) where S<:(AbstractArray{Bool,N} where N) at REPL[2]:3
Stacktrace:
[1] top-level scope at REPL[9]:1
我有一个函数接受 7 个(关键字)参数,每个参数都指定了类型,最后一个参数有默认值,如下所示:
function dummy(;truefalse1::S, somevar1::T, somevar2::T, somevar3::T, somevar4::T,
scalarvar::Int64, truefalse2::D = falses(3, 3)) where {
T <: Union{Array{Float64,2}, SubArray{Float64, 2}},
S <: AbstractArray{Bool}, D <: AbstractArray{Bool}}
###
end
truefalse*
参数可以是二维布尔数组 (BitArray{2}) 或它的视图(例如 view(somearray, 2:4, 3:5)
)。 somevar*
参数可以是 Float64 类型的二维数组或此类数组的“view
”。
以上有效,但这个看似等效的版本无效(测试输入见下文):
function dummy(;truefalse1::S, somevar1::T, somevar2::T, somevar3::T, somevar4::T,
scalarvar::Int64, truefalse2::S = falses(3, 3)) where {
T <: Union{Array{Float64,2}, SubArray{Float64, 2}},
S <: AbstractArray{Bool}}
###
end
(换句话说,D
类型已被删除,两次出现都使用 S
类型。)
报错信息如下:
ERROR: MethodError: no method matching #dummy#823(::SubArray{Bool,2,BitArray{2},Tuple{UnitRange{Int64},UnitRange{Int64}},false}, ::SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false}, ::SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false}, ::SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false}, ::SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false}, ::Int64, ::BitArray{2}, ::typeof(wheretonext))
Closest candidates are:
#dummy#823(::S, ::T, ::T, ::T, ::T, ::Int64, ::S, ::typeof(dummy)) where {T<:Union{Array{Float64,2}, SubArray{Float64,2,P,I,L} where L where I where P}, S<:(AbstractArray{Bool,N} where N)} at /SomePath/someDummyCode.jl:238
Stacktrace:
[1] (::var"#kw##dummy")(::NamedTuple{(:truefalse1, :somevar1, :somevar2, :somevar3, :somevar4, :scalarvar),Tuple{SubArray{Bool,2,BitArray{2},Tuple{UnitRange{Int64},UnitRange{Int64}},false},SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false},SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false},SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false},SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false},Int64}}, ::typeof(dummy)) at ./none:0
[2] top-level scope at none:0
这是测试输入的样本集:
julia> using Random; Random.seed!(1234);
julia> trf1 = rand(5, 10) .> rand(5, 10); trf2 = rand(5, 10) .> rand(5, 10);
julia> smv1 = rand(5, 10); smv2 = rand(5, 10); smv3 = rand(5, 10); smv4 = rand(5, 10);
这些情况使用第二个函数声明产生上述错误:
julia> dummy(truefalse1 = view(trf1, 2:4, 3:5), somevar1 = view(smv1, 2:4, 3:5),
somevar2 = view(smv2, 2:4, 3:5), somevar3 = view(smv3, 2:4, 3:5),
somevar4 = view(smv4, 2:4, 3:5), scalarvar = 1)
julia> dummy(truefalse1 = view(trf1, 2:4, 3:5), somevar1 = view(smv1, 2:4, 3:5),
somevar2 = view(smv2, 2:4, 3:5), somevar3 = view(smv3, 2:4, 3:5),
somevar4 = view(smv4, 2:4, 3:5), scalarvar = 1, truefalse2 = falses(3, 3))
但是,仍然使用第二个函数声明,这种情况下工作正常:
julia> dummy(truefalse1 = view(trf1, 2:4, 3:5), somevar1 = view(smv1, 2:4, 3:5),
somevar2 = view(smv2, 2:4, 3:5), somevar3 = view(smv3, 2:4, 3:5),
somevar4 = view(smv4, 2:4, 3:5), scalarvar = 1, truefalse2 = view(trf2, 2:4, 3:5))
(回想一下:以上所有测试用例都可以在第一个函数声明中正常工作。)
对于我可能做错了什么或 Julia 语言中的相关微妙之处的任何建议,我将不胜感激。这是我在 Julia 中编写代码的第二周,所以我也欢迎任何其他提示。谢谢!
这是因为在第一个定义中,你让truefalse1
和truefalse2
有不同的类型S
和D
,这两种类型都是[=16=的子类型].而在第二个定义中,truefalse1
和 truefalse2
必须具有相同的类型 S
(约束条件是 S
是 AbstractArray{Bool}
的子类型)。
Parametric Methods 的文档应该对此进行更详细的解释,但也许以下更简单的示例可以帮助您了解其工作原理:
# a and b can be of different types
function foo(a::S, b::T) where {
S<:AbstractArray{Bool},
T<:AbstractArray{Bool}}
end
# a and b must have the same type
function bar(a::S, b::S) where {
S<:AbstractArray{Bool}}
end
# test data
a = rand(Bool, 10); # Array
b = rand(Bool, 10); # Array
c = view(b, 1:5); # SubArray
以下调用均有效:
# OK because:
# - typeof(a) == Array{Bool,1} <: AbstractArray{Bool}
# - typeof(b) == Array{Bool,1} <: AbstractArray{Bool}
# => substitute S for Array{Bool,1} and T for Array{Bool,1}
julia> foo(a, b)
# OK because:
# typeof(a) == typeof(b) == Array{Bool,1} <: AbstractArray{Bool}
# => substitute S for Array{Bool,1}
julia> bar(a, b)
# OK because:
# - a isa Array{Bool,1} <: AbstractArray
# - c isa SubArray{Bool,...} <: AbstractArray{Bool}
# => substitute S for Array{Bool,1} and T for SubArray{Bool,...}
julia> foo(a, c)
# Not OK because typeof(a) != typeof(b)
# there is no concrete type S such that
# - a isa S
# - b isa S
# - S <: AbstractArray
# => Method does not match
julia> bar(a, c)
ERROR: MethodError: no method matching bar(::Array{Bool,1}, ::SubArray{Bool,1,Array
{Bool,1},Tuple{UnitRange{Int64}},true})
Closest candidates are:
bar(::S, ::S) where S<:(AbstractArray{Bool,N} where N) at REPL[2]:3
Stacktrace:
[1] top-level scope at REPL[9]:1