Julia可有可无的表达

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的值。例如:
Julia可有可无的表达

不过如果参数中不存在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不同。此处不做过多介绍。

酷客网相关文章:

赞(0)

评论 抢沙发

评论前必须登录!