Types Julia:抽象和具体类型Integer对Int8对Int64

Types Julia:抽象和具体类型Integer对Int8对Int64,types,julia,Types,Julia,假设我有一个整数n,它只取[0,10]中的值。对于64位系统,我应该将其声明为n::Integer作为一般性的,还是将其声明为n::Int8或n::UInt8作为简约的,还是将其声明为n::Int64 请澄清新手的原因,例如风格、性能 参考: 更新参考资料(2021):编辑:参考Stefan接受的答案。我的意思是,这是对函数分派中使用类型的响应,但事实上与我自己相矛盾(正如我明确指出的那样,函数分派实际上应该是整数) 我总是使用Int,只是为了一般性,但这取决于应用程序的性能有多关键。除非您明确

假设我有一个整数
n
,它只取
[0,10]
中的值。对于64位系统,我应该将其声明为
n::Integer
作为一般性的,还是将其声明为
n::Int8
n::UInt8
作为简约的,还是将其声明为
n::Int64

请澄清新手的原因,例如风格、性能

参考:


更新参考资料(2021):编辑:参考Stefan接受的答案。我的意思是,这是对函数分派中使用类型的响应,但事实上与我自己相矛盾(正如我明确指出的那样,函数分派实际上应该是整数)

我总是使用
Int
,只是为了一般性,但这取决于应用程序的性能有多关键。除非您明确需要,否则切勿
Int64
。许多函数在
Int
上分派,而不是在
Integer
上分派(尽管建议在抽象类型上分派),这意味着当传递一个UInt8时,它们将失败(因为
Int
有符号的
的子类型,
UInt
不是),因此过分热衷于类型将导致问题。
作为一般的经验法则,你不应该比你必须的更具体地对待类型。

区分两种不同的情况很重要

<强>存储:如果将一个类型存储为<代码> n>代码>作为它的一个字段,或者作为数组中的值,那么您应该明确地考虑使用<代码> It8或<代码> UIT88/COD>。即使单个值的节省空间可以忽略不计,但如果在集合中创建并存储了许多类型的实例,那么节省的空间也会很快变得显著。假设您有一个带有字段

n
Foo
类型,那么您可以执行以下操作:

struct Foo
    n::UInt8
end
将值分配给
Foo
对象的
n
字段时,该值将自动转换为
UInt8
,如果无法忠实转换该值,则会引发错误:

julia> Foo(123) # Ints are automatically converted to UInt8
Foo(0x7b)

julia> typeof(ans.n)
UInt8

julia> Foo(500) # if too large, an error is raised
ERROR: InexactError()
Stacktrace:
 [1] Foo(::Int64) at ./REPL[1]:2

julia> Foo(-1) # ditto if too small
ERROR: InexactError()
Stacktrace:
 [1] Foo(::Int64) at ./REPL[1]:2

julia> Foo(2π/π)
Foo(0x02)
如果分配的值已经是正确的类型,则不需要检查,因此没有开销

Dispatch:如果您正在编写一个函数的方法,该方法将
n
作为参数,那么在
n
参数上有一个语义上合理的松散类型注释是无害的。在您描述的例子中,似乎任何类型的整数值都是合理的,因此使用
n::integer
可能是合适的。例如,如果要为
Foo
对象实现选中的构造函数,可以执行以下操作:

struct Foo
    n::UInt8

    function Foo(n::Integer)
        0 <= n <= 10 || throw(ArgumentError("n not in [0, 10]: $n"))
        return new(n)
    end
end
Foo
构造适用于任何类型的整数,检查它是否在正确的范围内,然后转换为
UInt8
。这比
Foo
的内置构造函数稍微有点限制,它很乐意接受任何类型的
n
参数,并尝试将其转换为
UInt8
——即使参数不是整数类型。如果这种行为是可取的,您可以将这里的类型签名进一步放宽到
n::Real
n::Number
(甚至
n::Any
,尽管这似乎有些过分)


请注意,严格类型化的方法参数没有性能优势–实际参数类型的专用代码无论如何都是按需生成的。

对于新来者(如我自己)来说,阅读已清除的内容。此外,检查以下帮助(
Int-UIntInt64@PatrickT文件:(请注意,与Int不同,Float不作为特定大小AbstractFloat的类型别名存在。与整数寄存器不同,浮点寄存器大小由IEEE-754标准指定。而Int的大小反映了该计算机上本机指针的大小。)特定于分派的类型不会带来性能提升。是的,任何编写分派的函数至少应该执行
Int
Integer
更好,因为由于自动专门化,扩展函数定义没有成本,用户可以通过如果他们愿意,可以使用较小的整数类型。我相信我们在这里是同意的。请注意,LightGraphs.jl就是一个很好的例子。使用较小的类型或无符号类型可能会提供更好的性能,因此,对于函数参数,我将它们声明为
integer
,而不是
Int
,因为这可能限制太多。例如,如果您有泛型代码,它有一个测试
if foo>1000;…;end
,并且
foo
被声明为
Int
,那么它将始终具有该检查,但是,如果它被声明为
Integer
,那么如果您传入一个
Int8
,Julia可以告诉您该测试总是错误的,并去掉if块。(学习Julia时,这对我来说是最令人惊讶的事情)。对于结构(类型)中的字段,我会使用最小的具体类型,并将其布局为尽可能小的存储空间[例如,而不是
Int8,Int,Int8,Int32,Int16
,这将需要8+8+4+4(+4)=32字节,我有
Int,Int32,Int16,Int8,Int8
,它只需要16个字节],当结构在数组中使用时。此外,如果您事先不知道需要什么类型,您可以参数化类型。当我知道值不能为负值时,我还使用
UInt*
,这为编译器提供了更多信息,以便以后进行优化。感谢您将这一点表述得比我在但是,在具体的情况下,-您对我的印象有何评论?包中的许多函数都是按Int而不是按整数调度的,这可能会导致使用
UInt
s时出现问题?我认为这可能是个问题,但在这种情况下,调度签名可能应该是l
julia> Foo(123)
ERROR: ArgumentError: n not in [0, 10]: 123
Stacktrace:
 [1] Foo(::Int64) at ./REPL[26]:2

julia> Foo(3)
Foo(0x03)
julia> Int<:Integer
true

julia> UInt<:Integer
true
julia> Int64<:Int
true