Programación · Lenguajes de programación
Tutorial de Julia para principiantes
Respuesta corta
Julia es un lenguaje de alto nivel y de tipado dinámico diseñado para el cálculo numérico y científico rápido. Se lee como Python o MATLAB, pero compila cada función a código nativo la primera vez que se llama, por lo que los bucles pueden ejecutarse a velocidades cercanas a las de C. Su idea central es el despacho múltiple: una función elige su método según los tipos de todos sus argumentos. Instálalo con juliaup y ejecuta un archivo con julia hello.jl.
Qué es Julia y quién lo usa
Julia la crearon Jeff Bezanson, Stefan Karpinski, Viral Shah y Alan Edelman, y se hizo pública en 2012. La versión 1.0 llegó en 2018, y la serie 1.x mantiene la compatibilidad hacia atrás. Julia pretende resolver el «problema de los dos lenguajes»: prototipar en un lenguaje lento y fácil y luego reescribir las partes críticas en C o Fortran.
Julia la usan científicos, ingenieros y analistas de datos para simulación, ecuaciones diferenciales, optimización, estadística, aprendizaje automático y computación de alto rendimiento. Su ecosistema de paquetes cubre data frames, gráficos, computación en GPU y diferenciación automática.
Instala Julia y ejecuta Hola, mundo
El instalador recomendado es juliaup, que instala y actualiza versiones de Julia en Windows, macOS y Linux (consulta julialang.org/downloads). Tras la instalación, julia inicia el REPL y julia file.jl ejecuta un script.
# hello.jl
println("Hello, world!")julia hello.jl
# Hello, world!
julia
# julia> 1 + 2
# 3En el REPL, pulsa ] para entrar en el modo de paquetes (por ejemplo add DataFrames), ? para ver la ayuda de un nombre y ; para una shell. Pulsa Retroceso para volver al prompt normal.
Variables y tipos básicos
Las variables no necesitan declaración, pero cada valor tiene un tipo concreto que puedes inspeccionar con typeof. Puedes añadir anotaciones de tipo a los argumentos de las funciones y a los campos de los struct cuando quieras.
| Tipo | Ejemplo | Notas |
|---|---|---|
| Int64 | 42 | Entero predeterminado en sistemas de 64 bits; el desbordamiento da la vuelta |
| Float64 | 3.14, 1e-3 | Coma flotante predeterminada |
| Rational | 1//3 | Fracción exacta |
| String | "hello" | UTF-8; se interpola con "$name" |
| Char | 'a' | Un solo carácter Unicode |
| Bool | true, false | Valores lógicos |
| Symbol | :name | Un nombre internado, que se usa a menudo como clave u opción |
| Nothing | nothing | «Sin valor», lo devuelven las funciones sin resultado |
name = "Ana"
age = 30
println("$name is $age, next year $(age + 1)")
# Ana is 30, next year 31
typeof(age) # Int64
7 / 2 # 3.5 (always a float)
7 ÷ 2 # 3 (integer division, also div(7, 2))
2^10 # 1024Julia acepta nombres Unicode como π o α; en el REPL y en la mayoría de los editores escribes \pi y pulsas Tab para obtener π.
Control de flujo
Los bloques terminan con la palabra clave end; la sangría es solo para facilitar la lectura. if, for y while funcionan como en la mayoría de los lenguajes, y las comprensiones construyen arreglos en una línea.
function sign_name(n)
if n > 0
"positive"
elseif n < 0
"negative"
else
"zero"
end
end
for i in 1:3
println("$i squared is $(i^2)")
end
squares = [x^2 for x in 1:5 if isodd(x)] # [1, 9, 25]
x = 5
result = x > 3 ? "big" : "small" # "big"1:3 es un rango que incluye ambos extremos. La última expresión de una función o de un bloque if es su valor, así que sign_name(-2) devuelve "negative" sin un return explícito.
Funciones
add(x, y) = x + y # one-line definition
function greet(name = "world"; punctuation = "!")
return "Hello, $name$punctuation"
end
greet() # "Hello, world!"
greet("Ana", punctuation = ".") # "Hello, Ana."
square = x -> x^2 # anonymous function
map(square, [1, 2, 3]) # [1, 4, 9]
v = [3, 1, 2]
sort!(v) # v is now [1, 2, 3]Los argumentos posicionales van antes del punto y coma y los argumentos con nombre, después. Por convención, una función cuyo nombre termina en ! modifica uno de sus argumentos, como sort! o push!, mientras que sort devuelve un arreglo nuevo.
Arreglos, tuplas y diccionarios
| Estructura | Literal | Notas |
|---|---|---|
| Vector | [1, 2, 3] | Índice desde 1, mutable, crece con push! |
| Matrix | [1 2; 3 4] | Los espacios separan columnas, ; separa filas |
| Tuple | (1, "a") | Inmutable, de longitud fija |
| Named tuple | (name = "Ana", age = 30) | Se accede a los campos con .name |
| Dict | Dict("a" => 1) | Mapa hash mutable |
| Set | Set([1, 2, 2]) | Valores únicos |
v = [10, 20, 30]
v[1] # 10 (first element)
v[end] # 30
push!(v, 40)
A = [1 2; 3 4]
A * A # matrix product: [7 10; 15 22]
A .* A # element-wise: [1 4; 9 16]
v .+ 1 # broadcasting: [11, 21, 31, 41]
sqrt.([1, 4, 9]) # [1.0, 2.0, 3.0]La sintaxis con punto se llama broadcasting: f.(x) aplica f a cada elemento, y .+, .* operan elemento a elemento. Julia fusiona las cadenas de operaciones con punto en un solo bucle.
Característica distintiva: el despacho múltiple
Una función de Julia puede tener muchos métodos. Cuando la llamas, Julia elige el método más específico para los tipos de todos los argumentos. Esto sustituye a los métodos de clase: los tipos guardan datos y el comportamiento se añade definiendo métodos para ellos, incluso desde otro paquete.
abstract type Shape end
struct Circle <: Shape
r::Float64
end
struct Rect <: Shape
w::Float64
h::Float64
end
area(c::Circle) = π * c.r^2
area(r::Rect) = r.w * r.h
shapes = [Circle(1.0), Rect(2.0, 3.0)]
println(round.(area.(shapes), digits = 2)) # [3.14, 6.0]Como Julia compila una versión especializada de una función para los tipos de argumentos que ve, la primera llamada a una función es más lenta (incluye la compilación) y las siguientes son rápidas. Usa mutable struct cuando los campos deban cambiar después de crearse.
Manejo de errores
Julia usa excepciones. Lanza un tipo de excepción integrado como ArgumentError o DomainError, o define el tuyo como un struct que sea subtipo de Exception. tryparse devuelve nothing en lugar de lanzar una excepción.
function parse_age(s)
n = tryparse(Int, s)
n === nothing && throw(ArgumentError("not a number: $s"))
n < 0 && throw(DomainError(n, "age must not be negative"))
return n
end
try
parse_age("abc")
catch e
if e isa ArgumentError
println("Error: ", e.msg)
else
rethrow()
end
end
# Error: not a number: abcUn programa pequeño y completo: frecuencias de palabras
text = "the cat and the hat and the bat"
counts = Dict{String,Int}()
for w in split(text)
counts[w] = get(counts, w, 0) + 1
end
top = sort(collect(counts), by = last, rev = true)
for (word, n) in first(top, 2)
println(word, " ", n)
end
# the 3
# and 2split divide la cadena por espacios en blanco, get devuelve 0 por defecto para las palabras nuevas, collect convierte el diccionario en un vector de pares word => count, y by = last ordena por la cantidad.
Siguientes pasos
- El manual oficial de Julia en docs.julialang.org, empezando por «Getting Started».
- La página de aprendizaje de julialang.org, que lista tutoriales, libros y cursos.
- Lee el capítulo «Performance Tips» del manual antes de optimizar código.
- Explora paquetes como DataFrames.jl, Plots.jl y DifferentialEquations.jl.
Preguntas frecuentes
¿Julia es más rápido que Python?
En código con muchos bucles y trabajo numérico, Julia suele ser mucho más rápido que Python puro porque compila a código nativo. Python con bibliotecas como NumPy puede ser rápido en operaciones vectorizadas, así que la diferencia depende de la tarea.
¿Por qué Julia empieza a contar desde 1?
Julia sigue la convención de las matemáticas y de lenguajes como MATLAB, R y Fortran, en los que el primer elemento de un arreglo tiene el índice 1.
¿Por qué la primera ejecución de mi código en Julia es lenta?
Julia compila cada función para los tipos de argumentos la primera vez que se llama. Las siguientes llamadas reutilizan el código compilado, así que mide el rendimiento después de una llamada de calentamiento o con un paquete de benchmarking.
¿Julia es gratis?
Sí. Julia es de código abierto bajo la licencia MIT, y casi todos sus paquetes también lo son.