4

In Julia, I most often see code written like fun(n::T) where T<:Integer, when the function works for all subtypes of Integer. But sometimes, I also see fun(n::Integer), which some guides claim is equivalent to the above, whereas others say it's less efficient because Julia doesn't specialize on the specific subtype unless the subtype T is explicitly referred to.

The latter form is obviously more convenient, and I'd like to be able to use that if possible, but are the two forms equivalent? If not, what are the practicaly differences between them?

4

2 回答 2

5

是的, Bogumił Kamiński在他的评论中是正确的:f(n::T) where T<:Integer并且f(n::Integer)行为完全相同,除了前一种方法的名称T已经在其主体中定义。当然,在后一种情况下,您可以显式分配T = typeof(n),它将在编译时计算。

但是,在其他一些情况下,使用像这样的 TypeVar 至关重要,可能值得一提:

  • f(::Array{T}) where T<:Integer确实是很不一样f(::Array{Integer})。这是常见的参数不变性问题(文档和关于它的另一个 SO 问题)。
  • f(::Type)将为所有s生成一个专业化。DataType因为类型对 Julia 来说非常重要,所以Type类型本身是特殊的,并且允许参数化,例如Type{Integer}允许您指定Integer类型。您可以使用f(::Type{T}) where T<:Integer要求 Julia 专门研究Type它作为参数获得的确切类型,允许Integer或其任何子类型。
于 2018-05-02T18:19:28.037 回答
4

两个定义是等价的。通常,只有当您需要在代码中直接使用特定类型时,您才会使用fun(n::Integer)表单并应用。例如,考虑 Base 中的以下定义(所有以下定义也来自 Base),其中它具有自然用途:fun(n::T) where T<:IntegerT

zero(::Type{T}) where {T<:Number} = convert(T,0)

或者

(+)(x::T, y::T) where {T<:BitInteger} = add_int(x, y)

即使你在很多情况下需要类型信息,使用typeof函数也足够了。又是一个示例定义:

oftype(x, y) = convert(typeof(x), y)

即使您使用的是参数类型,您通常也可以避免使用where子句(有点冗长),例如:

median(r::AbstractRange{<:Real}) = mean(r)

因为你不关心函数体中参数的实际值。

现在 - 如果你是像我一样的 Julia 用户 - 问题是如何让自己相信这可以按预期工作。有以下几种方法:

  • 您可以检查一个定义是否覆盖了方法表中的另一个(即在评估两个定义之后,该函数只存在一种方法);
  • @code_typed您可以使用、或等检查两个函数生成的代码@code_warntype,并发现它是相同的@code_llvm@code_native
  • 最后,您可以使用基准测试代码的性能BenchmarkTools

在这里http://slides.com/valentinchuravy/julia-parallelism#/1/1有一个很好的情节来解释 Julia 对您的代码所做的事情(我也向任何 Julia 用户推荐整个演示文稿 - 它非常好)。您可以在上面看到,降低 AST 后的 Julia 在 LLVM 代码生成步骤之前应用类型推断步骤来专门化函数调用。

您可以提示 Julia 编译器以避免专门化。这是在 Julia 0.7 上使用@nospecialize宏来完成的(虽然这只是一个提示)。

于 2018-05-02T18:42:41.717 回答