Skip to the content.

Editor

Para escribir código propio la consola no alcanza. Necesitamos un archivo de texto que luego podemos correr desde la consola.

No existe un entorno de desarrollo especializada para Julia como pueden ser Spyder para Python, R Studio para R o Matlab en sí mismo. Para escribir código Julia más extenso es necesario utilizar un editor de texto plano externo: VSCode, Sublime, Atom, Kate, GEdit, Zed, etc., etc., etc. Cualquiera de ellos es capaz de reconocer la sintaxis de Julia (eventualmente vía plug-ins). Los esfuerzos de la comunidad de Julia han estado focalizados en el desarrollo del plug-in para VSCode, de modo que si no hay preferencias previas lo más sencillo es usar VSCode (o VS-Codium si se prefiere una versión de código abierto y des-microsofteada). A quienes gusten de editores que corren en la terminal, les recomiendo Helix.

Una vez instalado VSCode instalar también el plug-in de Julia.

Por otro lado, en una consola de Julia instalar el paquete LanguageServer. Para ello, tipear ] para ingresar en el gestor de paquetes y luego:

  pkg> add LanguageServer

El LanguageServer es un paquetito que contiene la descripción de la sintaxis de Julia. VSCode (y otros editores) ejecutan este paquete para poder reconocer el código Julia y ofrecer distintas herramientas (ayudas para autocompletar, firmas de funciones, etc.).

Archivos .jl

Para escribir código Julia sólo necesitamos generar un archivo con extensión .jl. Luego podemos correrlo desde la consola.

Generar un archivo nuevo e insertar el siguiente código:

function fibonacci(n)
    fib = zeros(n)
    fib[1] = 1; fib[2] = 1
    for i  3:n
        fib[i] = fib[i-1] + fib[i-2]
    end
    return fib
end

Se trata de una función muy sencilla que recibe un parámetro n y devuelve un vector con los primeros n términos de la sucesión de Fibonacci.

Guardamos el archivo, por ejemplo como clase2.jl (identificar en qué directorio queda guardado).

Luego podemos ejecutarlo desde una consola Julia (en VSCode y otras IDEs puede abrirse una terminal interna, dentro del propio programa).

Funciones

Continuando con el ejemplo, vale la pena notar:

Nota: La indentación no es obligatoria. Sin embargo, las recomendaciones de estilo indican que deben usarse 4 espacios.

Para ejecutar el archivo desde una sesión de Julia usamos la función include(). Esto equivale a copiar el código del archivo dentro de la consola.

  julia> include("clase2.jl")
  fibonacci (generic function with 1 method)

Recordar que para que esto funcione la consola debe estar posicionada en el directorio del archivo. Si no, es posible incluir el archivo poniendo toda la ruta en lugar de sólo el nombre. El texto fibonacci (generic function with 1 method) aparece porque la sesión interactiva siempre muestra el resultado de la última expresión ejecutada. En este caso, la única expresión ha sido la definición de la función. Se puede suprimir este mensaje poniendo ;: include("clase.jl");.

Hasta aquí sólo hemos generado la función dentro de la sesión interactiva. Podemos correrla, por ejemplo:

  julia> fibonacci(4)
  4-element Vector{Float64}:
    1.0
    1.0
    2.0
    3.0

El resultado obtenido no es el más deseable, dado que la sucesión de Fibonacci está formada por enteros. El problema viene de que zeros(n) genera, por defecto, un vector de ceros en flotante. Al cargarle enteros, Julia los promueve a flotantes para mantener el tipo del vector. Para obtener un vector de enteros, podemos hacer:

fib = zeros(Int,n)

Recargar el archivo y probar la función.

Como última observación, la segunda línea de la función podría usar broadcasting: fib[1:2] .= 1. El . antecede al operador de asignación e indica que la asignación debe ser casillero a casillero sobre el subvector formado por los dos primeros lugares de fib. Notar que al llamar a una función el . se coloca luego del nombre de la función (f.(x)), pero se pone antes de los operadores (.=).

Y ya que estamos hablando de broadcasting, ¿Qué cabe esperar si corremos el siguiente código?

  julia> fibonacci.([2,4,7])

¿Y el siguiente código?

  julia> fibonacci.((3,4,5))

If

Agreguemos la siguiente función a nuestro archivo:

function comparar(x,y)
    if x<y
        println("$x es menor que $y")
    elseif x>y
        println("$x es mayor que $y")
    else
        println("$x e $y son iguales")
    end
end      

Luego de recargar el archivo en la consola, correr el código:

  julia> w = comparar(5,4)
  julia> w
  julia> typeof(w)

¿Qué devuelve?

Los operadores booleanos usuales en Julia son:

Evaluaciones de circuito corto y operador ternario

Probar las siguientes secuencias de código

  julia> 1 == 1.0
  julia> 1 == 1 + 0im
  julia> 1 < 2
  julia> 1 < 2+0im
  julia> 1 > 2//3
  julia> 1 == 2//2
  julia> 1 === 1
  julia> 1 === 1.0
  julia> 1 === 1+0im
  julia> 1 === 2//2
  julia> isodd(4)
  julia> iseven(8)
  julia> isinteger(9)
  julia> isinteger(3.5)
  julia> isinteger(2//2)
  julia> x::UInt64 = 9
  julia> typeof(x)
  julia> typeof(x) == typeof(9)
  julia> isinteger(x)
  julia> z = println("es par")
  julia> z
  julia> typeof(z)
  julia> iseven(2) && println("es par")
  julia> iseven(3) && println("es par")
  julia> iseven(2) || println("es impar")
  julia> iseven(3) || println("es impar")

En el ejemplo anterior, los operadores && y || ¿están realmente haciendo una comparación de valores booleanos?

Pasemos en limpio:

Usemos este último concepto en un caso realista. En nuestro archivo con funciones, modificar la función fibonacci agregando la siguiente línea (inmediatamente debajo del encabezado de la función:) Esta herramienta se usa bastante en Julia. Un buen uso podría ser agregar la siguiente línea a la función fibonacci():

isinteger(n) || error("n debe ser entero, se pasó el valor $n de tipo $(typeof(n))")

Volver a cargar el archivo en la consola y correrlo con un dato no entero y con uno entero

  julia> include("clase1.jl")
  julia> fibonacci(2.5)
  julia> fibonacci(4)
  julia> fibonacci(12//4)

Probar el siguiente código:

  julia> isodd(3) ? println("es impar") : println("es par")
  julia> isodd(2) ? println("es impar") : println("es par")
  julia> esimpar(x) = isodd(x) ? println("es impar") : println("es par")
  julia> esimpar(4)
  julia> esimpar(7)
  julia> esparbit(x) = iseven(x) ? 1 : 0
  julia> esparbit(4)
  julia> esparbit(9)
  julia> x = rand(1:100,100);
  julia> println(x)
  julia> esparbit.(x)
  julia> sum(esparbit.(x))
  julia> x .|> esparbit |> sum
  julia> sum(esparbit,x)
  julia> sum(iseven,x)

Pasemos en limpio:

Ejercicio: Escribir la función partida que recibe un natural n y devuelve n÷2 si n es par y 3n+1 si es impar. Escribir dos versiones: una extensa, usando function con un if - else - end y una compacta, en una línea, usando el operador ternario.

While

La sintaxis del while sigue la misma lógica que el if:

    while condiciones
        instrucciones a repetir
    end

Ejercicio: La conjetura de Collatz dice que si aplicamos la función del ejercicio anterior sucesivamente comenzando por cualquier natural n, eventualmente se alcanzará el valor 1. Experimentemos para evaluar esta conjetura. Implementar una función cuya firma sea:

function verif_collatz(n)

que reciba un valor n y aplique reiteradamente la función anterior hasta que se alcance el valor 1. La función debe devolver la cantidad de iteraciones que fueron necesarias para llegar a 1.

Ejercicio: Si la conjetura fuera falsa, podríamos encontrar un n para el cual la aplicación reiterada de la función no concluya nunca. Para evitar eso podemos imponer un tope al número de iteraciones a realizar. Para ello, el encabezado de nuestra función podría ser

function verif_collatz(n,max_iter)

donde max_iter será el tope que impongamos al número de evaluaciones, agregando al while la condición i < max_iter (donde i es el contador de iteraciones). Esto no es del todo feliz, porque normalmente no nos interesa el valor de max_iter, sino sólo el de n. Una forma de evitar el problema es asignarle a max_iter un valor por defecto. Esto se logra cambiando la firma de la función por:

function verif_collatz(n,max_iter=1000)

De este modo la función puede ejecutarse con dos parámetros (n y max_iter), o sólo uno (n), en cuyo caso max_iter tomará el valor por defecto 1000. Implementar este cambio en la función.

Ejercicio: Escribir una función que reciba un parámetro N y genere (y devuelva) el vector de longitud N conteniendo la logitud de la sucesión de Collatz para cada n menor o igual que N. Calcular el vector para N=1_000_000 y decidir cuál es el valor de n que genera la sucesión más larga (se puede usar la función argmax).


<< Volver a la clase 1
>> Ir a la parte 2