许多语言都有类似 null 或 none 这样的方式来表示一个不存在的值。
Julia 的设计目标是处理大量(往往杂乱)的数据,因此它有多种表示“无”的形式。
总体目标是在遇到缺失或可疑的值时把它们标记出来,然后继续执行,而不是抛出异常。
nothing如果某个值确实不存在,就用nothing来表示。这可能最接近 C 语言(NULL)或 Python(None)的做法。
julia> n = nothing
julia> isnothing(n)
true
julia> typeof(n)
Nothing
所以nothing是类型为Nothing的单例值,我们可以对它进行检测。
nothing的一个常见用法,是作为那些只利用其副作用(打印、网络配置等等)的函数的返回(非)值。
missing对于值在理论上存在、但我们不知道它具体是什么的情况,就使用missing。
例如,在统计道路上行驶的车辆时,人工观察员可能需要休息,或者自动传感器会出故障,但车流仍在继续。
因此missing是一个占位符,提醒人们需要对如何处理数据中的这一空缺做出决定。
julia> mv = [1, 2, missing]
3-element Vector{Union{Missing, Int64}}:
1
2
missing
julia> typeof(mv)
Vector{Union{Missing, Int64}} (alias for Array{Union{Missing, Int64}, 1})
julia> ismissing.(mv) # broadcast function, displays as 1 for true, 0 for false
3-element BitVector:
0
0
1
很少有其他语言内置了这一特性,但相近的类似物有 R 中的NA或 SQL 中的NULL。
如果存在missing值,表达式通常默认返回missing。
如果你希望忽略这些值,可以使用skipmissing()函数来明确表达这一点:
julia> mv = [1, 2, missing]
3-element Vector{Union{Missing, Int64}}:
1
2
missing
julia> sum(mv) # missing in, missing out
missing
julia> skipmissing(mv)
skipmissing(Union{Missing, Int64}[1, 2, missing])
julia> collect(skipmissing(mv))
2-element Vector{Int64}:
1
2
julia> sum(skipmissing(mv)) # functions like sum() can work with iterators
3
因为skipmissing创建的是迭代器,所以如果你需要一个向量,就用collect()把它包起来。
有时把missing值替换为某个默认值是很有用的。
@coalesce()宏对此很有用,它会返回第一个非 missing 的值(如果没有其他值,则返回missing)。
julia> str = ["I", "exist", missing]
3-element Vector{Union{Missing, String}}:
"I"
"exist"
missing
julia> [@coalesce(s, "-") for s in str]
3-element Vector{String}:
"I"
"exist"
"-"
NaNNaN 是“Not a Number”的缩写,它标记的是在期望得到数字的情况下出现的计算问题。
julia> v = [0, 1, -1]
3-element Vector{Int64}:
0
1
-1
julia> v / 0
3-element Vector{Float64}:
NaN
Inf
-Inf
julia> sum(v / 0)
NaN
任何涉及 NaN 的计算都会得到NaN结果。
目前还没有专门用于移除NaN值的函数,但标准的filter()函数可以很轻松地做到这一点。
只有满足给定条件为true的值才会被复制到结果数组中:
julia> filter(!isnan, [1, 2, NaN])
2-element Vector{Float64}:
1.0
2.0
在这个练习中,你将编写代码,为工厂员工创建佩戴的工牌。员工有 ID、姓名和部门名称。员工工牌上的标签格式如下:"[id] - name - DEPARTMENT"。
实现print_name_badge函数。它接收 ID、姓名和部门,返回工牌标签,其中部门名称要用大写。
julia> print_name_badge(67, "Katherine Williams", "Strategic Communication")
"[67] - Katherine Williams - STRATEGIC COMMUNICATION"
由于计算机系统的一点小毛病,新员工刚开始在工厂工作时,有时还没有 ID。因为必须佩戴工牌,他们会拿到一个不带 ID 前缀的临时工牌。
扩展print_name_badge函数。当 ID 缺失时,它应该创建一个不带 ID 的工牌。
julia> print_name_badge(missing, "Robert Johnson", "Procurement")
"Robert Johnson - PROCUREMENT"
就连工厂老板也必须时刻佩戴工牌。不过,老板没有部门,也永远不会有部门:他们高于所有部门。这种情况下,标签应当返回"OWNER",而不是部门名称。
扩展print_name_badge函数。当部门为nothing时,就认为这个工牌属于公司老板。
julia> print_name_badge(204, "Rachel Miller", nothing)
"[204] - Rachel Miller - OWNER"
注意,老板也有可能同时是新员工。
julia> print_name_badge(missing, "Rachel Miller", nothing)
"Rachel Miller - OWNER"
为了大致衡量 ID 的发放情况,你想看看所有还没有 ID 的员工的工资总和。这个值很高,说明很多人在排队等待,或者问题波及到了资深员工:两种情况都很糟糕。
实现salaries_no_id函数,它接收一个 ID 向量和一个对应的工资向量,返回还没有 ID 的人的工资总和。两个向量的长度相同。
julia> ids = [204, missing, 210, 352, missing, 263]
julia> salaries = [23, 21, 47, 35, 17, 101] * 1000
julia> salaries_no_id(ids, salaries)
38,000