不可变类型
最简单的复合类型是不可变类型。不可变类型的实例(如元组 )是值。它们的字段在创建后无法更改。在许多方面,不可变类型就像 Tuple
,它具有类型本身和每个字段的名称。
单例类型
根据定义,复合类型包含许多更简单的类型。在朱莉娅,这个数字可以为零; 也就是说,允许不可变类型不包含任何字段。这与空元组 ()
相当。
为什么这有用呢?这种不可变类型被称为单例类型,因为它们中只有一个例子可能存在。这些类型的值称为单值。标准库 Base
包含许多这样的单例类型。这是一个简短的清单:
Void
,nothing
的类型。我们可以验证Void.instance
(这是检索单例类型的单例值的特殊语法)确实是nothing
。- 任何媒体类型,例如
MIME"text/plain"
,都是具有单个实例MIME("text/plain")
的单例类型。 Irrational{:π}
,Irrational{:e}
,Irrational{:φ}
和类似类型是单例类型,它们的单例实例是无理值π = 3.1415926535897...
等。- 迭代器大小特征
Base.HasLength
,Base.HasShape
,Base.IsInfinite
和Base.SizeUnknown
都是单例类型。
Version >= 0.5.0
- 在 0.5 及更高版本中,每个函数都是单例类型的单例实例! 像任何其他单例值一样,我们可以从
typeof(sin).instance
恢复函数sin
。
由于它们不包含任何内容,因此单例类型非常轻量级,并且编译器可以经常对它们进行优化,以避免运行时开销。因此,它们非常适合于特征,特殊标记值以及人们想要专注的功能。
要定义单例类型,
julia> immutable MySingleton end
要为单例类型定义自定义打印,
julia> Base.show(io::IO, ::MySingleton) = print(io, "sing")
要访问单例实例,
julia> MySingleton.instance
MySingleton()
通常,人们会将此指定为常数:
julia> const sing = MySingleton.instance
MySingleton()
包装类型
如果零字段不可变类型是有趣且有用的,那么也许单字段不可变类型甚至更有用。这些类型通常称为包装器类型,因为它们包装一些底层数据,为所述数据提供备用接口。Base
中包装类型的一个例子是 String
。我们将定义一个类似于 String
的类型,命名为 MyString
。这种类型将由字节的向量(一维数组 )支持(UInt8
)。
首先,类型定义本身和一些自定义显示:
immutable MyString <: AbstractString
data::Vector{UInt8}
end
function Base.show(io::IO, s::MyString)
print(io, "MyString: ")
write(io, s.data)
return
end
现在我们的 MyString
型已经可以使用了! 我们可以为它提供一些原始的 UTF-8 数据,并按我们喜欢的方式显示:
julia> MyString([0x48,0x65,0x6c,0x6c,0x6f,0x2c,0x20,0x57,0x6f,0x72,0x6c,0x64,0x21])
MyString: Hello, World!
显然,这种字符串类型在变得像 Base.String
类型一样可用之前需要做很多工作。
真正的复合类型
也许最常见的是,许多不可变类型包含多个字段。一个例子是标准库 Rational{T}
类型,它包含两个 fieds:分子的 num
字段和分母的 den
字段。模拟这种类型的设计相当简单:
immutable MyRational{T}
num::T
den::T
MyRational(n, d) = (g = gcd(n, d); new(n÷g, d÷g))
end
MyRational{T}(n::T, d::T) = MyRational{T}(n, d)
我们已成功实现了一个简化有理数的构造函数:
julia> MyRational(10, 6)
MyRational{Int64}(5,3)