在Julia中,当函数适用于fun(n::T) where T<:Integer
的所有子类型时,我经常会看到类似Integer
的代码。但有时候,我也看到fun(n::Integer)
,有些指南认为它与上述相同,而其他人则认为效率较低,因为朱莉娅并不专注于特定的子类型,除非子类型T是明确的提到。
后一种形式显然更方便,如果可能,我希望能够使用它,但这两种形式是否相同?如果没有,它们之间的实际差异是什么?
答案 0 :(得分: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})
非常不同。这是常见的参数不变性问题(docs和another SO question关于它。)f(::Type)
将为所有DataType
生成一个专门化。由于类型对Julia非常重要,因此Type
类型本身很特殊,并允许参数化Type{Integer}
等参数化,以允许您指定 Integer
类型。您可以使用f(::Type{T}) where T<:Integer
要求Julia专注于它作为参数获取的Type
的确切类型,允许Integer
或其任何子类型。答案 1 :(得分:4)
两个定义都是等价的。通常,只有当您需要在代码中直接使用特定类型fun(n::Integer)
时,才会使用fun(n::T) where T<:Integer
表单并应用T
。例如,考虑Base中的以下定义(所有以下定义也来自Base),它具有自然用途:
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
一个很好的情节解释了Julia对你的代码所做的事情http://slides.com/valentinchuravy/julia-parallelism#/1/1(我还向所有Julia用户推荐整个演示文稿 - 非常棒)。并且您可以看到,在降低AST之后,Julia应用类型推断步骤来专门化LLVM codegen步骤之前的函数调用。
您可以提示Julia编译器以避免专门化。这是使用Julia 0.7上的@nospecialize
宏完成的(虽然它只是一个提示)。