Tracks
/
Julia
Julia
/
Ejercicios
/
Gurú de las calificaciones
Gurú de las calificaciones

Gurú de las calificaciones

Ejercicio de aprendizaje

Introducción

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.

  1. Julia tiene types, que son centrales en su diseño.
  2. Por lo general, Julia puede «adivinar» el tipo, usando Type Inference.

El compilador JIT revisará (todo) el código, verá cómo se usa una variable e inferirá un tipo predeterminado adecuado y compatible con ese uso.

Asignación de tipos

Confiar en la inferencia de tipos está bien para resolver ejercicios sencillos de tutorial, pero para programas más grandes es probable que necesites un control más preciso.

En la mayoría de los procesadores modernos, un entero tiene como valor predeterminado Int64. Vimos en el concepto Numbers que un valor se puede convertir a un tipo particular que no es el predeterminado.

julia> x = Int16(42)
42
julia> typeof(x)
Int16

Sin embargo, a la variable x todavía se le puede asignar un tipo diferente:

julia> x = "changed"
"changed"
julia> typeof(x)
String

Esto es la type instability, que es:

  • Muy conveniente en scripts pequeños.
  • Mala para el rendimiento y la fiabilidad en programas más grandes.

En cambio, 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.

La jerarquía de tipos

Int64, Int16, String, Char: ¿de dónde «vienen» estos tipos?

En muchos lenguajes orientados a objetos (OO), cada tipo es una clase, las subclases los organizan en una jerarquía de clases y los métodos de clase definen los comportamientos.

Java y Ruby son ejemplos obvios de este patrón, pero incluso Python es similar internamente.

Julia no tiene clases.

La razón documentada es que las características de OO 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)

Añadiendo algunos detalles:

  • 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.
  • Hay una jerarquía de tipos que sube por 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 es único por 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 por explorar.

Cómo comprobar tipos

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 la jerarquía 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

Tipos abstractos y concretos

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:

  1. Los nodos con subtipos se llaman abstract.
  2. Los nodos hoja, sin subtipos, se llaman 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.

Instrucciones

En este ejercicio, vas a hacer un poco de ingeniería de datos al preprocesar algunos datos de calificaciones. Una escuela con muchos, muchos, muchos (¿demasiados?) estudiantes está tratando de hacer un análisis de datos de las calificaciones que reciben los estudiantes y quiere hacerlo de manera eficiente, así que la idea es minimizar y organizar los datos.

La escala de calificaciones suele ser de 0 a 10, así que lo más eficiente es usar un tipo de dato con solo ocho bits por calificación.

  • Algunos profesores usan números reales (porque están obsesionados con la precisión), y vas a tener que convertirlos al tipo de dato UInt8.
  • Otros profesores usan números enteros con valores negativos (para castigar a los estudiantes especialmente revoltosos), y en estos casos vas a tener que convertirlos al tipo de dato Int8.

Una vez que hayas implementado la función para convertir las calificaciones, vas a tener que escribir otra para manejar las colecciones que las contienen, degradando las calificaciones y devolviéndolas en orden descendente.

  • Algunos profesores usan Vectors ordenados de forma ascendente para guardar sus calificaciones, porque son meticulosos con sus datos.
  • Otros profesores usan Sets, que no están ordenados, para guardar sus calificaciones, porque son un poco más perezosos.

En ambas funciones, vas a tener que manejar los argumentos no válidos lanzando un MethodError.

Note

El manejo de excepciones se cubrirá en un concepto posterior, así que para efectos 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 que se pasan a la función. Con la función demote(n), f es demote y args es (n,):

throw(MethodError(demote, (n,)))

1. Degrada las calificaciones

Implementa el método demote(n).

  • Con un argumento de tipo Float64, debe redondear hacia arriba al número entero más cercano y devolver un dato de tipo UInt8.
  • Con un Integer arbitrario, debe devolver el mismo entero en un dato de tipo Int8.
  • Cualquier otro argumento debe lanzar un MethodError.
julia> demote(4.2)::UInt8
5

julia> demote(4)::Int8
4

julia> demote("hi")
MethodError: no method matching demote(::String)     #output truncated

2. Preprocesa los datos

Implementa el método preprocess(coll).

  • Con un argumento de tipo Vector, debe degradar todos los números e invertir el vector.
  • Con un argumento de tipo Set, debe degradar todos los números y devolver un vector ordenado de forma descendente.
  • Cualquier otro argumento debe lanzar un MethodError.
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
Editar en GitHub El enlace se abre en una ventana o una pestaña nuevas
Julia Exercism

¿Todo listo para empezar Gurú de las calificaciones?

Regístrate en Exercism para aprender y dominar Julia con 35 conceptos128 ejercicios y mentoría humana real, todo gratis.