Багато мов мають спосіб позначити неіснуюче значення, наприклад 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
Мало які інші мови мають цю можливість вбудованою, але близькі аналоги - це NA в R або NULL у SQL.
Тип вектора в прикладі вище задано як 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.