En el concepto Vectors, dijimos que «los arrays están en el corazón del lenguaje Julia» y que un vector es unidimensional.
Teniendo esto en cuenta, es razonable esperar que el lenguaje ofrezca muchas formas versátiles y potentes de hacer cosas con vectores, sea lo que sea que eso signifique.
Algunas funciones muy sencillas reciben un vector como entrada y devuelven un valor escalar como salida.
v = [2, 3, 4]
length(v) # => 3
sum(v) # => 9
Supón que tienes un vector numérico y quieres restar 0,5 a cada valor.
julia> v = [1.2, 1.5, 1.7]
3-element Vector{Float64}:
1.2
1.5
1.7
julia> v - 0.5
ERROR: MethodError: no method matching -(::Vector{Float64}, ::Float64)
Eso falla, así que ¿qué pasa si restamos otro vector?
julia> v - [0.5, 0.5, 0.5]
3-element Vector{Float64}:
0.7
1.0
1.2
Funciona, pero resulta bastante tedioso y consume mucha memoria a medida que los vectores se alargan.
Por suerte, Julia tiene un punto «mágico» que resuelve este problema de forma muy sencilla: v .- 0.5 es todo lo que necesitas.
La siguiente sección explica por qué.
Así que v - 0.5 falla, pero v .- 0.5 funciona, y necesitamos entender qué hace el punto.
Dos cosas, que se combinan para dar el resultado deseado.
En primer lugar, añadir un punto delante de cualquier operador infijo significa «aplicar esta operación a cada elemento por separado».
De forma similar, añadir un punto después del nombre de una función la «vectoriza», aunque la función se haya escrito para entradas escalares.
julia> sqrt.([1, 4, 9])
3-element Vector{Float64}:
1.0
2.0
3.0
En un ejemplo anterior vimos que podemos restar vectores de la misma longitud, aunque conviene entender que .- es un operador más seguro que -, porque deja clara la intención de operar elemento a elemento.
julia> v .- [0.5, 0.5, 0.5]
3-element Vector{Float64}:
0.7
1.0
1.2
¿Y si los vectores tienen longitudes distintas?
julia> v .- [0.5, 0.5]
ERROR: DimensionMismatch: arrays could not be broadcast to a common size
julia> v .- [0.5,]
3-element Vector{Float64}:
0.7
1.0
1.2
En general, que las longitudes no coincidan es un error, excepto cuando una tiene longitud 1 (técnicamente, una dimensión «singleton»).
Los singleton como [0.5,] o simplemente 0.5 se expanden automáticamente a la longitud necesaria mediante repetición.
Esto es el núcleo de broadcasting.
La selección de elementos de un vector por número de índice se ha tratado en conceptos anteriores.
a = collect('A':'Z') # => 26-element Vector{Char}
# index with an integer
a[2] # => 'B'
# index with a range
a[12:2:18] # => ['L', 'N', 'P, 'R']
# index with another vector
a[ [1, 3, 5] ] # => ['A', 'C', 'E']
También es posible seleccionar elementos que satisfacen cierta expresión lógica (técnicamente, un «predicado»). Esto suele requerir broadcasting.
julia> a[a .< 'D']
3-element Vector{Char}:
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase)
'C': ASCII/Unicode U+0043 (category Lu: Letter, uppercase)
En expresiones más complejas, los puntos tienden a multiplicarse (pero son pequeños y fáciles de escribir).
julia> a[a .< 'D' .|| a .> 'W']
6-element Vector{Char}:
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase)
'C': ASCII/Unicode U+0043 (category Lu: Letter, uppercase)
'X': ASCII/Unicode U+0058 (category Lu: Letter, uppercase)
'Y': ASCII/Unicode U+0059 (category Lu: Letter, uppercase)
'Z': ASCII/Unicode U+005A (category Lu: Letter, uppercase)
Recuerda que el «vector» puede ser, de hecho, cualquier iterable ordenado adecuado, como un rango:
julia> n = 3:10
3:10
julia> n[isodd.(n)]
4-element Vector{Int64}:
3
5
7
9
Eres un entusiasta observador de aves que lleva un registro de cuántos pájaros han visitado tu jardín en los últimos siete días.
Tienes seis tareas, todas relacionadas con el número de pájaros que han visitado tu jardín.
Implementa la función today() para que devuelva cuántos pájaros han visitado hoy tu jardín.
Los recuentos de pájaros están ordenados por día: el primer elemento es el recuento del día más antiguo y el último es el de hoy.
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> today(birds_per_day)
1
Implementa la función increment_todays_count() para incrementar el recuento de hoy:
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> increment_todays_count(birds_per_day)
[2, 5, 0, 7, 4, 2]
Implementa la función has_day_without_birds() que devuelve true si hubo un día en el que ningún pájaro visitó el jardín; en caso contrario, devuelve false:
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> has_day_without_birds(birds_per_day)
true
Implementa la función count_for_first_days() que devuelve el número de pájaros que han visitado tu jardín desde el comienzo de la semana, pero limita el recuento al número de días indicado desde el comienzo de la semana.
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> count_for_first_days(birds_per_day, 4)
14
Algunos días están más ajetreados que otros.
Un día ajetreado es aquel en el que cinco o más pájaros han visitado tu jardín.
Implementa la función busy_days() para que devuelva el número de días ajetreados:
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> busy_days(birds_per_day)
2
Decides ampliar tus registros guardando los recuentos de varias semanas. En cada caso, los recuentos están ordenados por día de la semana, desde el lunes como primera entrada hasta el domingo como última.
Implementa la función average_per_day() que devuelve la media de 2 semanas.
julia> week1 = [7, 2, 9, 1, 3, 0, 10]
julia> week2 = [2, 6, 4, 1, 3, 8, 9]
julia> average_per_day(week1, week2)
[4.5, 4.0, 6.5, 1.0, 4.0, 3.0, 9.5]
Regístrate en Exercism para aprender y dominar Julia con 35 conceptos128 ejercicios y mentoría humana real, todo gratis.