多くの言語には、存在しない値を表すためにnullやnoneといった仕組みがあります。
Juliaは大量の(しばしば乱雑な)データを扱えるように設計されているため、存在しないことを表す方法を複数持っています。
全体的なねらいは、欠けていたり疑わしい値に出会った時点でそれに印を付け、例外を発生させずに処理を続けることです。
nothing値が本当に存在しない場合、それはnothingで表します。これはおそらく、C言語のNULLやPythonのNoneに最も近いものです。
julia> n = nothing
julia> isnothing(n)
true
julia> typeof(n)
Nothing
つまりnothingはNothing型のシングルトンの値で、それかどうかを判定できます。
nothingのよくある使い方の1つは、副作用(出力、ネットワーク設定など)のためだけに使われる関数の戻り値(実際には値なし)としてです。
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が近い存在です。
上の例のベクトル型はUnion{Missing, Int64}になっており、これは他の言語のOptionやMaybeといった型と少し似ています。
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"
"-"
NaN"Not a Number"の略であるNaNは、数値が期待される場面で計算上の問題が起きたことを示します。
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
これは、使う機会が比較的少ないものです。 例外として、次のようなものがあります。
structのような複合型を、初期化されていないフィールドのまま作成する方法があります。
Juliaは通常デフォルト値を使おうとしますが、フィールドの型を推論できるだけの情報がある場合に限ります。undefで初期化するのが柔軟で高性能な方法になります。
たとえば、循環バッファ演習のコミュニティ解答で使われています(高度なテクニックですが、これは難しい演習として位置づけられています)。値にアクセスしようとしてundefが返ってくるのはエラー状態で、通常はUndefRefErrorで知らされます。