Até agora, o syllabus não disse muito sobre tipos, mas é claro que eles existem em Julia:
julia> vals = (42, 4.3, π, "hello", 'Q')
(42, 4.3, π, "hello", 'Q')
julia> typeof(vals)
Tuple{Int64, Float64, Irrational{:π}, String, Char}
Nunca especificamos os tipos, mas Julia os atribuiu de qualquer forma.
types, que são centrais no design da linguagem.Type Inference.O compilador JIT vai percorrer (todo) o código, ver como uma variável é usada e inferir um tipo padrão adequado, compatível com esse uso.
Contar com a inferência de tipos dá conta de exercícios simples de tutorial, mas, em programas maiores, você provavelmente vai precisar de um controle mais preciso.
Na maioria dos processadores modernos, um inteiro tem Int64 como tipo padrão.
Vimos no Conceito Numbers que um valor pode ser convertido para um tipo específico diferente do padrão.
julia> x = Int16(42)
42
julia> typeof(x)
Int16
No entanto, a variável x ainda pode ser reatribuída a um tipo diferente:
julia> x = "changed"
"changed"
julia> typeof(x)
String
Isso é type instability, que é:
Em vez disso, podemos definir o tipo de x usando o operador :::
julia> y::Int16 = 42
42
julia> typeof(y)
Int16
julia> y = "changed"
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int16
The function `convert` exists, but no method is defined for this combination of argument types.
Agora y é do tipo Int16, e sempre será.
Assim, o compilador sabe quantos bytes reservar para ela e pode otimizar o restante do código contando com um tipo estável.
Nesse aspecto, a variável é mais ou menos parecida com as de uma linguagem de tipagem estática, como C.
Int64, Int16, String, Char: de onde "vêm" esses tipos?
Em muitas linguagens orientadas a objetos (OO), cada tipo é uma classe, a criação de subclasses as organiza em uma hierarquia de classes, e os métodos de classe definem os comportamentos.
Java e Ruby são exemplos óbvios desse padrão, mas até o Python é parecido internamente.
Julia não tem classes.
O motivo documentado é que os recursos de orientação a objetos interferem no compilador JIT e prejudicam o desempenho em tempo de execução.
E, mesmo assim, veja este código:
julia> y::Int16 = 42
42
julia> typeof(y)
Int16
julia> supertype(Int16)
Signed
julia> supertypes(Int16)
(Int16, Signed, Integer, Real, Number, Any)
Detalhando um pouco:
Int16 é um tipo, e podemos criar variáveis desse tipo.Int16 é um subtipo de Signed, e a função supertype() nos mostra isso.Integer, Real e Number até Any, no topo, e subtypes() vai listar esse ramo da hierarquia para nós.Todos os ramos terminam em Any, que é único por ser seu próprio supertipo.
julia> supertypes(String)
(String, AbstractString, Any)
julia> supertype(Any)
Any
Então, Julia não tem uma hierarquia de class, mas tem uma hierarquia de type.
Mais adiante, vamos tentar destrinchar como isso funciona, mas há muito mais para explorar antes.
Podemos usar typeof() para testar igualdade da maneira habitual.
julia> typeof(11)
Int64
julia> typeof(11) == Int64
true
julia> typeof(11) == Number
false
A igualdade de tipos precisa ser exata, pois essa forma de comparação não entende nada da hierarquia de tipos.
De forma mais flexível, isa nos diz se um valor tem o mesmo tipo do que está sendo comparado ou um subtipo dele.
Ele pode ser usado tanto na forma de função quanto na forma infixa.
julia> 12 isa Int64
true
julia> 12 isa Number
true
julia> isa(12, Number)
true
julia> 12 isa String
false
Repare que isa espera um value à esquerda, não um type.
Tentar comparar dois tipos dessa forma vai dar resultados inesperados.
O operador correto é <:, que vamos ver muito mais em conceitos futuros.
julia> Int64 isa Number ## Don't do this!
false
julia> Int64 <: Number
true
Vimos que a hierarquia de tipos forma uma estrutura de árvore (no sentido da Ciência da Computação, com a raiz no topo).
Cada item da árvore é um node, e eles podem ser divididos em categorias:
abstract.concrete.julia> subtypes(Integer) # an abstract type
3-element Vector{Any}:
Bool
Signed
Unsigned
julia> subtypes(Int64) # a concrete type
Type[]
Essa é uma distinção importante, porque só tipos concretos podem ser instantiated como variáveis.
julia> a::Int16 = 42
42
julia> typeof(a)
Int16
julia> b::Integer = 42
42
julia> typeof(b)
Int64
Repare que tentar usar um tipo abstrato não gera mensagem de erro (neste caso), mas o compilador cria um tipo concreto adequado: Int64 em vez de Integer.
Neste exercício, você vai fazer um pouco de engenharia de dados ao pré-processar alguns dados de notas. Uma escola com muitos, muitos, muitos (alunos demais?) alunos está tentando fazer uma análise de dados das notas recebidas pelos alunos e quer fazer isso de forma eficiente, então a ideia é minimizar e organizar os dados.
A escala de notas geralmente vai de 0 a 10, então é mais eficiente usar um tipo de dados com apenas oito bits por nota.
UInt8.Int8.Depois de implementar a função para converter as notas, você vai precisar escrever outra para lidar com as coleções em que elas são guardadas, rebaixando as notas e retornando-as em ordem decrescente.
Vectors ordenados em ordem crescente para guardar suas notas, porque são meticulosos com seus dados.Sets, que não são ordenados, para guardar suas notas, porque são um pouco mais preguiçosos.Nas duas funções, você vai precisar tratar entradas inválidas lançando um MethodError.
O tratamento de exceções será abordado em um Conceito posterior, então, para os propósitos deste exercício, você pode usar a seguinte sintaxe:
throw(MethodError(f, args))
Onde f é a função e args é uma tupla com os argumentos passados para a função.
Na função demote(n), f é demote e args é (n,):
throw(MethodError(demote, (n,)))
Implemente o método demote(n).
Float64, ele deve arredondar para cima até o número inteiro mais próximo e retornar um tipo de dados UInt8.Integer arbitrário, ele deve retornar o mesmo inteiro em um tipo de dados Int8.julia> demote(4.2)::UInt8
5
julia> demote(4)::Int8
4
julia> demote("hi")
MethodError: no method matching demote(::String) #output truncated
Implemente o método preprocess(coll).
Vector, ele deve rebaixar todos os números e inverter o vetor.Set, ele deve rebaixar todos os números e retornar um vetor ordenado em ordem decrescente.julia> preprocess([1, 2, 3])
3-element Vector{Int8}:
3
2
1
julia> preprocess(Set([2.2, 5.8, 3.4]))
3-element Vector{UInt8}:
6
4
3
julia> preprocess(42)
MethodError: no method matching preprocess(::Int64) #output truncated
Crie sua conta no Exercism para aprender e dominar Julia com 35 conceitos128 exercícios e mentoria humana de verdade, tudo de graça.