在Julia中,如何在调用者提供的(超)类型的参数上正确调度方法?

时间:2014-11-10 16:01:03

标签: julia

我想定义一个函数f(x, t::Type),它根据isa(x, t)执行不同的行为。我们想说如果是b1(x),我会打电话给b2(x),否则会function f(x, t::Type) if isa(x, t) b1(x) else b2(x) end end

我知道我可以在运行时进行动态检查,如下所示:

f{T}(x::T, t::Type{T}) = b1(x)
f(x, t::Type) = b2(x)

但是,有没有办法纯粹使用参数化方法调度?例如,如果我定义

f(1, Int)

对于f(1.0, Int)t,会调用正确的行为。但我希望这也适用于f(1, Number) 的所有子类型:

b2

这实际上会调用f,因为f(x::Number, t::Type{Number}) = b1(x)的第一个签名不匹配。有趣的是,f{T}(x::T, t::Type{T})在这种情况下会匹配。

我错过了一些明显的东西吗?

这显然是一个错误,并在0.4中修复。


问题:

  1. 为什么f(1, Number)T不匹配,即使Numberf{T2, T1 <: T2}(x::T1, t::Type{T2}))的类型替换匹配?< /秒>

  2. 使用t或类似的东西不起作用,因为只有在关闭完整的静态参数列表后,所有静态参数才会出现在范围内。为什么呢?

  3. 使用动态方法是否有任何性能损失?

  4. 如何将方法定义为内部函数,所以我可以将function f(x, t::Type); g(x::t) = b1(x); g(x) = b2(x); g(x) end绑定到局部变量,如下所示: {{1}}

    这有效,但性能成本是多少?

  5. 解决这个问题的惯用/首选方式是什么?

  6. (我在0.3.2上试过这个。)

1 个答案:

答案 0 :(得分:5)

回答你的问题:

  1. AFAICT它应该。正如用户3580870评论的那样,这似乎适用于Julia 0.4。
  2. 这是“三角调度”,尚未实施。见#3766
  3. 取决于。编译器可以在编译时评估isa(x, t)并消除分支。但是,使用isa可能还有一些不太可能的方法:
    • 如果两个分支返回不同的类型,则类型推断将无法正确推断返回类型,因为它在isa(x, t)成为常量之前发生。 (这可能代价高昂;下面其他可能的去优化可能不是什么大问题。)
    • 如果一个分支执行堆分配但另一个分支没有执行堆分配,则可能会发出不必要的GC帧。
    • 该功能可能未内联。
  4. 内部函数将具有与使用isa等相同的性能问题。从Julia 0.5开始,内部函数在这种情况下应该与顶级函数一样高效
  5. 提交错误;-)。我认为这应该有效。有可能无论如何固定在0.4中都可以向后移植到0.3。但如果不这样做,isa并不是一个糟糕的方法,前提是它不会导致类型推断问题。