Julia可有可无的表达,NaN
是浮点型常量,表示非数值对象,属于数值层面的概念;nothing
类型的单例常量,表示什么都没有,是语法层面的定义;missing
则表示数据区某个字段值的缺失,是数据的一种状态。
但有一种场景这三者都不能合理表述:例如某个名为x的变量,类型为T,在处理过程中,其值不断地变化的;很有可能在某个环节出现了空值(缺失)的情况,比如从外部文件加载数据时,因为数据无效导致值无法提供,x只能置空;但又不总是出现,也有值可用的情况,这种状况在大规模数据中经常会遇到。
但此时x的表达就出现了困难:一则未必总是浮点型,二则顺利的情况下也会有确切的值。为了能够表达这种“可有可无”的情况,可以Union{T, Nothing}
进行表达。这种方式等效于其他语言中的Nullable、Option或Maybe类型,而且这种类型联合结构能够用于函数的参数,复合类型的成员字段,甚至是数组的元素类型。
如果值本身的正常取值是nothing,但也存在缺失值,则可以使用Union{Some{T}, Nothing}
表达。当x==nothing
时,表示值缺失;而当x==Some(nothing)
时,则表示该值存在,只不过取值为nothing对象。在处理中,类似于SQL中的COALESCE函数,Julia的something()函数可以返回参数表中第一个不是nothing的值。例如:
不过如果参数中不存在nothing之外的有效值,该函数会抛出ArgumentError异常。对于某个Some对象,可以使用.value
获得其内部有效值。
除此之外,在Nullables.jl
包中,提供了一种自v6.0版本中抽离出来的单独维护的Nullable类型,也可以处理这种可有可无的情况。其中的Nullable类型,并不表示具体的空值,而是某个值存在可能为空、也可能不空的两可情况。其结构为:
Nullable{T} <: Any
hasvalue::Bool
value::T
其中,T是类型参数;hasvalue标记其是否有值存储,而value则记录着T类型的数据内容。
若要使用该类型,只需调用其构造函数便可创建一个Nullable对象,例如:
julia> Nullable{Int64}() # 空的情况,即这个Int64对象里没内容
Nullable{Int64}()
julia> Nullable{Tuple}() # 空的情况,即这个Tuple对象里没内容
Nullable{Tuple}()
或者是自定义的复合类型:
julia> struct MyType
x
y
end
julia> Nullable{MyType}() # 空的情况,即这个MyType对象里没内容
Nullable{MyType}()
当然可以将已有的值封装进去:
julia> a = MyType(1,2.3)
MyType(1, 2.3)
julia> m = Nullable(1)
Nullable{Int64}(1)
julia> n = Nullable((1,2,3))
Nullable{Tuple{Int64, Int64, Int64}}((1, 2, 3))
julia> p = Nullable(a)
Nullable{MyType}(MyType(1, 2.3))
也支持NaN与nothing这两个特殊值,即:
julia> q = Nullable(NaN)
Nullable{Float64}(NaN)
julia> r = Nullable(nothing)
Nullable{Nothing}(nothing)
对于Nullable对象,随时可以使用isnull()
函数判断其状态。如果无值,该函数会返回true,否则返回false,例如:
julia> isnull(Nullable{Int64}())
true
julia> isnull(Nullable{Tuple}())
true
julia> isnull(m)
false
julia> isnull(p)
false
有个特别情况需要注意,即:
julia> isnull(Nullable(NaN)) # Float64类型,有内容,但为NaN
false
julia> isnull(Nullable(nothing)) # Void类型,有内容,但为nothing
false
从此例可对Nullable作进一步理解:Nullable表达的仅仅是封装类型在物理上是否有内容;至于内容本身的意义,Nullable并不关心。
对于无元素的元组、数组或其他数集类型,Nullable同样不会判定为无内容,即:
julia> isnull( Nullable( () ) )
false
julia> isnull( Nullable( [] ) )
false
因为虽然无元素,但数集对象是确实存在的。
一旦发现某个Nullable对象是有内容的,便可使用get()
函数取得该内容:
julia> get(m)
1
julia> get(p)
MyType(1, 2.3)
julia> get(q)
NaN
julia> get(r) # 取的是nothing,但正如前所述,什么都不打印
# 空白
但若实际并无内容,会报出异常,例如:
julia> get(Nullable{Float64}())
ERROR: NullException()
这样能够确保调试运行期间及时发现问题,并可通过异常机制进行妥善处理。
如果应用中Nullable封装的数据不是关键内容,有则用,无则遵循惯例,所以在使用函数get()时可提供默认值,这样的话会更为便利。例如:
julia> get(Nullable{Float64}(), 0.0) # 为空,取0.0
0.0
julia> get(Nullable(1.0), 0.0) # 不为空,取实际值,忽略默认值
1.0
默认值的类型必须与Nullable
封装的类型一致,否则会导致结果不可靠甚至引发错误。
参数化类型Nullable虽然可在数据处理层面提供一种安全机制,但不能表达语法意义上的无效情况。例如,某些变量、字段或元素没有初始化便被访问,程序会立即抛出异常,但不能通过Nullable的封装来解决问题,而需要用isdefined()函数预先测试以便进一步处理。
此外,Julia的Base模块中还有一个Ptr{Nothing}类型的常量C_NULL,用于混合编程时兼容C语言的NULL指针,概念也与Nullable不同。此处不做过多介绍。
酷客网相关文章:
评论前必须登录!
注册