En el Concepto Vectors, dijimos que «los arrays están en el corazón del lenguaje Julia» y que un vector es unidimensional.
Dado esto, podríamos esperar razonablemente que el lenguaje proporcione muchas maneras versátiles y potentes de hacer cosas con vectores, sea lo que sea que eso signifique.
Algunas funciones muy simples reciben un vector como argumento y devuelven un resultado escalar.
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é tal si restas otro vector?
julia> v - [0.5, 0.5, 0.5]
3-element Vector{Float64}:
0.7
1.0
1.2
Funciona, pero es bastante tedioso y consume mucha memoria a medida que los vectores se hacen más largos.
Afortunadamente, Julia tiene un punto «mágico» para resolver este problema de forma muy sencilla: v .- 0.5 es todo lo que necesitas.
La siguiente sección explica por qué.
Entonces, 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, agregar un punto antes de cualquier operador infijo significa «aplicar esta operación a cada elemento por separado».
De manera similar, agregar un punto después de un nombre de función la «vectoriza», incluso si la función se escribió para argumentos escalares.
julia> sqrt.([1, 4, 9])
3-element Vector{Float64}:
1.0
2.0
3.0
Vimos en un ejemplo anterior que podemos restar vectores de igual longitud, aunque ten en cuenta que .- es un operador más seguro que -, ya que deja clara la intención de hacerlo elemento a elemento.
julia> v .- [0.5, 0.5, 0.5]
3-element Vector{Float64}:
0.7
1.0
1.2
¿Y qué pasa con los vectores de longitud desigual?
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, las longitudes desiguales son un error, excepto cuando uno tiene longitud 1 (técnicamente, una dimensión «singleton»).
Los singletons como [0.5,] o simplemente 0.5 se expanden automáticamente a la longitud necesaria mediante repetición.
Esto está en el corazón del 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 alguna expresión lógica (técnicamente, un «predicado»). Esto normalmente requiere 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)
Para expresiones más complejas, los puntos tienden a proliferar (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)
Un recordatorio de que el «vector» puede ser, de hecho, cualquier iterable ordenado apropiado, como un rango:
julia> n = 3:10
3:10
julia> n[isodd.(n)]
4-element Vector{Int64}:
3
5
7
9
Eres un entusiasta de la observación de aves y llevas la cuenta de cuántas aves han visitado tu jardín en los últimos siete días.
Tienes seis tareas, todas relacionadas con el número de aves que visitaron tu jardín.
Implementa la función today() para que devuelva cuántas aves visitaron tu jardín hoy.
Los conteos de aves están ordenados por día: el primer elemento es el conteo del día más antiguo y el último elemento es el conteo 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 conteo 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 ninguna ave visitó el jardín; de lo 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 aves que han visitado tu jardín desde el comienzo de la semana, pero limita el conteo a la cantidad de días indicada 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 son más ajetreados que otros.
Un día ajetreado es aquel en el que cinco o más aves 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 llevando conteos de varias semanas. En cada caso, los conteos 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 el promedio 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.