= 2 x
2
En estos primeros ejemplos veremos los rudimentos de la sintaxis de Julia. Y empezaremos con los básicos aunque dado que existen amplias coincidencias entre Julia y Python, asumiendo que el lector conoce las estructuras base de Python (listas, diccionarios, etc..) nos centraremos en las grandes diferencias.
Veamos con una de las primeras acciones que será la definición de variables.
= 2 x
2
Como vemos Julia tiene tipado dinámico, no tenemos por qué definir el tipo de datos asociado al valor que hemos introducido.
typeof(x)
Int64
Podemos hacer lo mismo con múltiples tipos
= 2 + 1im
x typeof(x)
Complex{Int64}
Aunque podemos incluir el tipado si fuera necesario.
::Float16 = 120
ytypeof(y)
Float16
Esto impedirá que hagamos cosas como
y = "hola"
con un error MethodError: Cannot convert an object of type String to an object of type Float16 en nuestro caso, ya que el tipo declarado de la variable y el valor a asociar no coinciden. Trabajaremos principalmente con los tipos habituales
Int64
Float64
Bool
String
Podemos crear tipos específicos para nuestras necesidades basado en esos tipos básicos.
struct Usuario
::String
nombre::String
apellido::Int64
anio_nacimientoend
Usuario("Iraitz","Montalbán", 1984)
Usuario("Iraitz", "Montalbán", 1984)
Como vemos , Julia tiene por costumbre imprimir el valor de la última línea ejecutada. Por definición son estructuras inmutables, por lo que si queremos variar sus datos una vez inicializados deberemos indicarlo expresamente.
mutable struct UsuarioMutable
::String
nombre::String
apellido::Int64
anio_nacimientoend
= UsuarioMutable("Iraitz","Montalban", 1984)
iraitz = "Montalbán" iraitz.apellido
"Montalbán"
Las variables booleanas nos permiten operaciones lógicas:
true !
false
true && false
false
O de pertenencia a un grupo o tipo, igualdad ==
y desigualdad !=
, <
, etc.
6 isa Real
true
Las listas de datos funcionan de forma muy similar a las listas en Python.
= [1, 2, 3] a
3-element Vector{Int64}:
1
2
3
Vemos que el tipo por defecto se llama Vector{}
y en corchetes marca nuestro tipo para los elementos. Esto implica que si un elemento requiere un tipado mayor, este se les asignará también al resto, siendo Any
(cualquiera) la opción más general.
= [1, 2, "hola"] a
3-element Vector{Any}:
1
2
"hola"
Podemos separar en filas nuestros elementos y así generar un vector de dos dimensiones o matriz.
= [1 2 3; 4 5 6] a
2×3 Matrix{Int64}:
1 2 3
4 5 6
Y podemos así extender a las dimensiones que necesitemos nuestros elementos multidimensionales. El acceso a los datos se realiza por índices o rangos, teniendo en cuenta el número de índices de nuestro vector. Quizás una de las cuestiones más relevantes viniendo de lenguajes como Python es que Julia cuenta lso elementos iniciando en 1 en lugar de 0. Por lo tanto pedir el elemento a[0]
nos dará un error.
1] a[
1
1:2] a[
2-element Vector{Int64}:
1
4
:, 1:2] a[
2×2 Matrix{Int64}:
1 2
4 5
2, 2] a[
5
Tampoco funcionarán los índices negativos ya que deberemos usar la palabra reservada end
.
end, end] a[
6
Otro aspecto clave son las funciones. La lógica de nuestro programa que toma variables y devuelve variables basado en operaciones lógicas.
function nombre_funcion(arg1, arg2)
= hacemos cosas con arg1 y arg2
resultado return resultado
end
También permite la definición mediate asignación, a modo de función lambda f_name(arg1, arg2) = hacemos cosas con arg1 y arg2
aunque por claridad emplearemos el modelo anterior.
Julia permite especificar el tipo de argumentos haciendo que dos funciones con el mismo nombre apliquen distinta lógica en base a los argumentos de entrada y su tipo.
function redondeo(x::Int64)
print("Es un entero")
return x
end
function redondeo(x::Float64)
print("Es un número con decimales")
return round(x)
end
methods(redondeo)
Esto nos permite invocar a uno u otro únicamente en base al tipado.
= Float64(18)
x
redondeo(x)
Es un número con decimales
18.0
En estos casos, si el tipado no coincide con ninguna de las definiciones de la función, Julia nos alertará con un error.
Podemos también definir valores por defecto en los argumentos de entrada y devolver más de un elemento.
function suma_y_multiplica(x::Int64, y::Int64 = 10)
= x + y
suma = x * y
mult return suma, mult
end
suma_y_multiplica(5)
(15, 50)
suma_y_multiplica(5, 5)
(10, 25)
En caso de querer utilizar argumentos clave (keyword) estos se separan mediante el carácter ;
y siempre deben disponer de un valor por defecto.
function suma_y_multiplica(x::Int64; y::Int64 = 10)
= x + y
suma = x * y
mult return suma, mult
end
suma_y_multiplica(5, y=7)
(12, 35)
Por último, en Julia existe la convención de añadir el carácter !
en caso de que la función altere o modifique alguno de sus argumentos.
function suma_uno!(V)
for i in eachindex(V)
+= 1
V[i] end
return nothing
end
suma_uno! (generic function with 1 method)
No devolvemos nada pero si evaluamos el contenido de nuestro dato inicial, veremos que ha sido modificado.
= [1, 2, 3]
datos
suma_uno!(datos)
datos
3-element Vector{Int64}:
2
3
4
Existen toda una serie de funciones que podemos emplear directamente desde la consola. Tenemos operadores básicos como suma o multiplicación, y otras funcionalidades no tan básicas como la selección de los primeros o últimos elementos de un vector, la creación de números aleatorios o el poder fijar una semilla para dicha generación aleatoria.
Como último apunte, veréis que existen sentencias que iniciamos con el carácter @
. Estas son conocidas como macros, operaciones que nos permiten simplificar la sintaxis y que en muchos casos hacen referencia a funciones con el mismo nombre.
@time print("Hemos tardado en imprimir esta línea")
Hemos tardado en imprimir esta línea 0.000030 seconds (10 allocations: 64.281 KiB)
macro es_entero(x)
if typeof(x) == Int64
return "Es un entero"
end
return "No es un entero"
end
@es_entero 5
"Es un entero"
Más que listar todas estas operaciones y estructuras disponibles, iremos viendo con ejemplos las más usuales según avancemos en los materiales.