Hasta ahora, el temario no ha dicho mucho sobre los tipos, pero está claro que existen en Julia:
julia> vals = (42, 4.3, π, "hello", 'Q')
(42, 4.3, π, "hello", 'Q')
julia> typeof(vals)
Tuple{Int64, Float64, Irrational{:π}, String, Char}
Nunca especificamos los tipos, pero Julia los asignó de todos modos.
types, que son fundamentales en su diseño.Type Inference.El compilador JIT examinará (todo) el código, verá cómo se usa una variable e inferirá un tipo por defecto adecuado y compatible con ese uso.
Confiar en la inferencia de tipos está bien para resolver ejercicios sencillos de tutorial, pero en programas más grandes es probable que necesites un control más preciso.
En la mayoría de los procesadores modernos, un número entero tiene Int64 por defecto.
Vimos en el concepto Numbers que se puede convertir un valor a un tipo concreto distinto del predeterminado.
julia> x = Int16(42)
42
julia> typeof(x)
Int16
Sin embargo, la variable x todavía se puede reasignar a un tipo diferente:
julia> x = "changed"
"changed"
julia> typeof(x)
String
Esto es type instability, que es:
En su lugar, podemos establecer el tipo de x usando el 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.
Ahora y es, y siempre será, de tipo Int16.
Así, el compilador sabe cuántos bytes reservar para ella y puede optimizar el resto del código para apoyarse en un tipo estable.
En esto, la variable es más o menos similar a las de un lenguaje de tipado estático como C.
Int64, Int16, String, Char: ¿de dónde «vienen» estos tipos?
En muchos lenguajes orientados a objetos (OO), cada tipo es una clase, la creación de subclases las organiza en una jerarquía de clases y los métodos de clase definen los comportamientos.
Java y Ruby son ejemplos evidentes de este patrón, pero incluso Python es similar internamente.
Julia no tiene clases.
La razón documentada es que las características de la programación orientada a objetos interfieren con el compilador JIT y perjudican el rendimiento en tiempo de ejecución.
Y, sin embargo, mira 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)
Con algunos detalles más:
Int16 es un tipo, y podemos crear variables de este tipo.Int16 es un subtipo de Signed, y la función supertype() nos lo muestra.Integer, Real y Number hasta Any en la cima, y subtypes() nos listará esta rama de la jerarquía.Todas las ramas terminan en Any, que tiene la particularidad de ser su propio supertipo.
julia> supertypes(String)
(String, AbstractString, Any)
julia> supertype(Any)
Any
Así que Julia no tiene una jerarquía de class, pero sí tiene una jerarquía de type.
Con el tiempo, intentaremos desentrañar cómo funciona esto, pero antes hay mucho más que explorar.
Podemos usar typeof() para comprobar la igualdad de la forma habitual.
julia> typeof(11)
Int64
julia> typeof(11) == Int64
true
julia> typeof(11) == Number
false
La igualdad de tipos debe ser exacta, ya que esta forma de comparación no entiende de jerarquías de tipos.
De forma más flexible, isa nos dirá si un valor tiene el mismo tipo que un comparador o un subtipo de este.
Se puede usar tanto en forma de función como de infijo.
julia> 12 isa Int64
true
julia> 12 isa Number
true
julia> isa(12, Number)
true
julia> 12 isa String
false
Ten en cuenta que isa espera un value a la izquierda, no un type.
Intentar comparar dos tipos de esta forma dará resultados inesperados.
El operador correcto es <:, que veremos mucho más en conceptos futuros.
julia> Int64 isa Number ## Don't do this!
false
julia> Int64 <: Number
true
Vimos que la jerarquía de tipos forma una estructura de árbol (en el sentido de las ciencias de la computación, con la raíz arriba).
Cada elemento del árbol es un node, y estos se pueden dividir en categorías:
abstract.concrete.julia> subtypes(Integer) # an abstract type
3-element Vector{Any}:
Bool
Signed
Unsigned
julia> subtypes(Int64) # a concrete type
Type[]
Esta es una distinción importante, porque solo los tipos concretos se pueden instantiated como variables.
julia> a::Int16 = 42
42
julia> typeof(a)
Int16
julia> b::Integer = 42
42
julia> typeof(b)
Int64
Ten en cuenta que intentar usar un tipo abstracto no da ningún mensaje de error (en este caso), pero el compilador crea un tipo concreto adecuado: Int64 en lugar de Integer.
En este ejercicio vas a hacer un poco de ingeniería de datos preprocesando algunos datos de calificaciones. Una escuela con muchos, muchos, muchos (¿demasiados?) estudiantes está intentando hacer un análisis de datos de las calificaciones que reciben los estudiantes y busca hacerlo de forma eficiente, así que la idea es minimizar y organizar los datos.
La escala de calificaciones suele ir de 0 a 10, así que lo más eficiente es usar un tipo de datos con solo ocho bits por calificación.
UInt8.Int8.Una vez que hayas implementado la función para convertir las calificaciones, tendrás que escribir otra para manejar las colecciones en las que se guardan, degradando las calificaciones y devolviéndolas en orden descendente.
Vectors ordenados de forma ascendente para guardar sus calificaciones, porque son meticulosos con sus datos.Sets, que no están ordenados, para guardar sus calificaciones, porque son un poco más perezosos.En ambas funciones tendrás que manejar las entradas no válidas lanzando un MethodError.
El manejo de excepciones se tratará en un Concepto posterior, así que, para los fines de este ejercicio, puedes usar la siguiente sintaxis:
throw(MethodError(f, args))
Donde f es la función y args es una tupla con los argumentos de entrada de la función.
Con la función demote(n), f es demote y args es (n,):
throw(MethodError(demote, (n,)))
Implementa el método demote(n).
Float64, debe redondear hacia arriba al número entero más cercano y devolver un tipo de datos UInt8.Integer arbitrario, debe devolver el mismo entero en un tipo de datos Int8.julia> demote(4.2)::UInt8
5
julia> demote(4)::Int8
4
julia> demote("hi")
MethodError: no method matching demote(::String) #output truncated
Implementa el método preprocess(coll).
Vector, debe degradar todos los números e invertir el vector.Set, debe degradar todos los números y devolver un vector ordenado en orden descendente.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
Regístrate en Exercism para aprender y dominar Julia con 35 conceptos128 ejercicios y mentoría humana real, todo gratis.