O

O nada em Julia

1 exercício

Sobre O nada

Muitas linguagens têm uma forma como null ou none para indicar um valor inexistente. Como Julia foi concebida para lidar com grandes volumes de dados (muitas vezes desorganizados), tem várias formas de inexistência.

O objetivo geral é sinalizar valores em falta ou suspeitos à medida que são encontrados, e depois continuar sem lançar uma exceção.

nothing

Se um valor realmente não existe, é representado por nothing. Isto é provavelmente o mais próximo do que C (NULL) ou Python (None) fariam.

julia> n = nothing

julia> isnothing(n)
true

julia> typeof(n)
Nothing

Assim, nothing é um valor singular do tipo Nothing, e podemos testá-lo.

Um uso comum de nothing é como um valor de retorno (ou não-valor) para funções que são usadas apenas pelos seus efeitos secundários (impressão, configuração de rede, ou outra coisa qualquer).

missing

Para situações em que um valor existe em teoria, mas não sabemos qual é, usa-se missing. Por exemplo, ao contar veículos que viajam numa estrada, os observadores humanos podem precisar de uma pausa ou os sensores automáticos podem avariar, mas o trânsito continua a fluir.

Assim, missing é um marcador de posição, alertando os humanos de que precisam de tomar uma decisão sobre como lidar com esta lacuna nos dados.

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

Poucas outras linguagens têm esta funcionalidade incorporada, mas equivalentes próximos são NA em R ou NULL em SQL.

O tipo de vetor no exemplo acima é definido como Union{Missing, Int64}, que tem alguma semelhança com tipos como Option ou Maybe noutras linguagens.

As expressões normalmente devolvem missing por predefinição se estiverem presentes valores missing. Se quiseres que estes valores sejam ignorados, usa a função skipmissing() para tornar isto explícito:

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

Como skipmissing cria um iterador, envolve-o em collect() se precisares de um vetor.

Por vezes, é útil substituir valores missing por um valor predefinido. A macro @coalesce() é útil para isto, pois devolve o primeiro valor não-missing (ou missing se não houver mais nada).

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

Abreviatura de "Not a Number", NaN sinaliza um problema de cálculo em situações em que se esperava um número.

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

Qualquer tipo de cálculo sobre dados que incluam um NaN dará um resultado NaN.

Atualmente, não existe uma função especial para remover valores NaN, mas a função padrão filter() pode fazer isto de forma bastante simples. Apenas os valores para os quais uma determinada condição seja true serão copiados para o array resultante:

julia> filter(!isnan, [1, 2, NaN])
2-element Vector{Float64}:
 1.0
 2.0

Dados indefinidos

Isto é algo que provavelmente vais usar com menos frequência. As exceções incluem:

  • Há formas de criar tipos compostos (como struct) com campos não inicializados. Julia normalmente tenta usar valores predefinidos, mas só o pode fazer se tiver informação suficiente para inferir o tipo do campo.
  • Em alguns casos, inicializar um vetor como undef pode ser uma abordagem flexível e eficiente. Isto tem sido usado, por exemplo, em soluções da comunidade para o exercício Buffer Circular (uma técnica avançada, mas este é classificado como um exercício difícil).

Obter um resultado undef de uma tentativa de aceder a um valor é um estado de erro, tipicamente sinalizado com um UndefRefError.

Editar via GitHub A ligação abre numa nova janela ou separador

Aprende O nada