假设我有一个整数n
,它只能取[0, 10]
中的值。我是应该将其声明为n::Int
来概括,n::Int8
或n::UInt8
是简约还是n::Int64
对于64位系统?
请澄清新手的原因,例如:风格,表现。
背景:我正在学习朱莉娅并很快迷失在type
,immutable
,syntactic sugar
等词汇中!我想我读过这个:https://docs.julialang.org/en/release-0.5/manual/integers-and-floating-point-numbers/
答案 0 :(得分:13)
区分两种不同的情况很重要。
存储:如果您的某个类型存储n
作为其中一个字段,或者作为数组中的值存储,那么您一定要考虑使用Int8
或UInt8
。即使单个值的节省空间可以忽略不计,如果创建了许多类型的实例并将其存储在集合中,那么节省的空间也会迅速变得显着。假设您的Foo
类型带有字段n
,那么您可以这样做:
struct Foo
n::UInt8
end
如果将值分配给n
对象的Foo
字段,它将自动转换为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
如果给出[0,10]之外的值,则抛出错误:
julia> Foo(123)
ERROR: ArgumentError: n not in [0, 10]: 123
Stacktrace:
[1] Foo(::Int64) at ./REPL[26]:2
julia> Foo(3)
Foo(0x03)
此Foo
构造适用于任何类型的整数,检查它是否在正确的范围内,然后转换为UInt8
。这比Foo
的内置构造函数稍微限制一些,它会愉快地使用任何类型的n
参数并尝试将其转换为UInt8
- 即使参数不是整数类型。如果需要这种行为,您可以在此处进一步放宽类型签名n::Real
,n::Number
(甚至n::Any
,尽管这似乎过多了。)
请注意,紧密键入的方法参数没有性能优势 - 实际参数类型的专用代码无论如何都是按需生成的。
答案 1 :(得分:4)
编辑:参考Stefan接受的答案。我的意思是这是对函数调度中类型的使用的响应,但实际上与我自相矛盾(正如我明确说明函数调度实际上应该是Integer)。
我总是使用Int
,只是为了它的一般性,但这取决于你的应用程序的性能关键程度。除非您明确需要,否则永远不会Int64
。许多函数在Int
而不是Integer
上调度(虽然建议是在抽象类型上调度),这意味着它们在传递UInt8时会失败(因为Int
是{的子类型{1}}和Signed
不是因为对类型过于热心会导致问题。
作为一般的经验法则,你应该永远不会对类型有所了解。
答案 2 :(得分:2)
对于新来者(如我自己),阅读abstract type docs可以使事情变得清晰起来。此外,检查以下帮助(<:
读取“是'的子类型”):
julia> Int<:Integer
true
julia> UInt<:Integer
true
和
julia> Int64<:Int
true