cv.cm

Programación · Lenguajes de programación

Tutorial de Julia para principiantes

6 min de lectura

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
# 3

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

TipoEjemploNotas
Int6442Entero predeterminado en sistemas de 64 bits; el desbordamiento da la vuelta
Float643.14, 1e-3Coma flotante predeterminada
Rational1//3Fracción exacta
String"hello"UTF-8; se interpola con "$name"
Char'a'Un solo carácter Unicode
Booltrue, falseValores lógicos
Symbol:nameUn nombre internado, que se usa a menudo como clave u opción
Nothingnothing«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          # 1024

Julia 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

EstructuraLiteralNotas
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
DictDict("a" => 1)Mapa hash mutable
SetSet([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: abc

Un 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 2

split 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.

Todos los tutoriales de programación