【问题标题】:Julia fails to multiple dispatchJulia 多次分派失败
【发布时间】:2017-09-22 14:17:01
【问题描述】:

v06 我想写一个需要 2 到 3 个参数的签名。第一个是整数或整数向量。第二个是整数向量或整数矩阵。第三个是整数向量或未指定。

我第一次是这样尝试的

function foo(
a::Union{Integer, Vector{Integer}},
b::Union{Vector{Integer}, Matrix{Integer}},
c::Union{Void, Vector{Integer}} = nothing)

当我这样称呼它foo(3, [0o7, 0o5]) 时,我收到一条错误消息,告诉我它无法匹配。

ERROR: LoadError: MethodError: no method matching foo(::Int64, ::Array{UInt8,1})
Closest candidates are:
  foo(::Union{Array{Integer,1}, Integer}, !Matched::Union{Array{Integer,1}, Array{Integer,2}}) at ...
  foo(::Union{Array{Integer,1}, Integer}, !Matched::Union{Array{Integer,1}, Array{Integer,2}}, !Matched::Union{Array{Integer,1}, Void}) at ...

现在我明白为什么 julia 无法匹配这个Array{UInt8} <: Array{Integer} == false,但这似乎是 julia 的不聪明。

然后我尝试了这个

foo(a::Union{Z1, Vector{Z1}},
    b::Union{Vector{Z2}, Matrix{Z2}},
    c::Union{Void, Vector{Z3}} = nothing
    ) where {Z1 <: Integer, Z2 <: Integer, Z3 <: Integer}

现在朱莉娅甚至不告诉我什么不匹配!

ERROR: LoadError: MethodError: no method matching foo(::Int64, ::Array{UInt8,1}, ::Void)
Closest candidates are:
  foo(::Union{Array{Z1<:Integer,1}, Z1<:Integer}, ::Union{Array{Z2<:Integer,1}, Array{Z2<:Integer,2}}, ::Union{Array{Z3<:Integer,1}, Void}) where {Z1<:Integer, Z2<:Integer, Z3<:Integer} at ...
  foo(::Union{Array{Z1<:Integer,1}, Z1<:Integer}, ::Union{Array{Z2<:Integer,1}, Array{Z2<:Integer,2}}) where {Z1<:Integer, Z2<:Integer} at ...

【问题讨论】:

  • 当你有像这样的非常复杂的类型签名时,尤其是有很多联合时,这可能表明你应该把它分成几个单独的方法定义。特别是,您可能希望至少避免使用foo(a, b, c=nothing),而支持foo(a, b, c)foo(a, b)。另外,还要考虑类型之间是否存在联系,例如只有当bMatrix 时,是a 还是Vector
  • @DNF 我理解您的担忧。问题是,如果我这样做,用户会收到无用的 julia 错误消息,而不是我编写的有用的错误消息。例如。 “没有方法存在 b 且 c 为 Void 的矩阵”,我宁愿这样“如果将 b 指定为矩阵,则必须将 c 指定为具有这些维度的向量”。事实上,这个签名是在检查所有内容并将所有内容转换为正确类型后调用真实方法的废话检查签名。
  • 您正在设置自己进行大量显式输入检查,这与多分派的想法背道而驰。为了避免让人们接触到普通的 Julia 错误消息,你给自己造成了很大的痛苦。另外,如果我根本没有提供任何c,我会惊讶地告诉cVoid!建议:将您的函数拆分为具有正确签名的单独方法。然后制作一个(或多个)后备方法来捕获其余部分:f(a, b, c) = ... 不带类型,并让其找出输入的问题并发出错误。
  • 不确定你想要什么,但类似:foo(a::Integer, b::Vector{&lt;:Integer}) = ... endfoo(a::Vector{&lt;:Integer}, b::Matrix{&lt;:Integer}, c::Vector{&lt;:Integer}) = ... endfoo(a::Integer, b...) = error("If a is an Integer, b must be a Vector of Integers.") endfoo(a::Vector{&lt;:Integer}, b...) = error("If a is an Vector of Integers, b must be a Matrix of Integers and c must be a Vector of Integers...") end(为了便于阅读,添加了结尾,实际上使用它们并不正确。)我认为这会实际上更容易编写好的错误消息。
  • 对于我目前的问题,我认为这样做没有好处。我不相信这会使我的代码更具可读性。

标签: julia


【解决方案1】:

是的,Array{UInt8} &lt;: Array{Integer} == false。这称为“参数不变性”。许多其他问题都讨论了这个话题。

不过,您遇到的另一个问题是,当您使用静态函数参数(即 f(…) where T)时,T 必须匹配一些东西,因为它可以使用在函数体中。这会导致Unions 出现问题,其中T 并非在每个选项中都可用。我相信关于更改此行为以允许匹配不包含 TUnion 元素存在一个未解决的问题,如果您尝试访问它,这会将绑定变成未定义的变量。

目前的解决方法是使用 不是 函数静态参数的类型变量。例如,

   foo(a::Union{Integer, Vector{<:Integer}},
       b::Union{Vector{<:Integer}, Matrix{<:Integer}},
       c::Union{Void, Vector{<:Integer}} = nothing) = 1

【讨论】:

  • IIRC,在过去不支持三角调度的时候,我们必须定义一个 typealias 来解决这个问题,例如typealias IntVector{T&lt;:Integer} Vector{T}foo(x::Union{Integer, IntVector}) = x。很高兴看到不再需要它!
猜你喜欢
  • 2019-03-26
  • 2022-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多