Одна з головних цілей Julia - вміти швидко й ефективно виконувати обчислення над числовими даними. Дуже багатьма числовими даними.
Зазвичай дані зберігаються в масивах (або в якомусь спорідненому типі), тож цілком слушно сказати, що масиви лежать в основі мови Julia.
Масиви можуть бути довільного розміру (обмежені лише памʼяттю обладнання) і мати довільну кількість вимірів.
Деякі типи масивів мають особливі назви (запозичені з лінійної алгебри).
Одновимірний масив називається Vector, а двовимірний - Matrix.
Обидва є підтипами Array у Julia.
Ця вступна концепція зосереджується на основах векторів. Багатовимірні масиви ми розглянемо пізніше в силабусі.
У Julia вектор - це впорядкована послідовність значень, до яких можна звертатися за номером індексу.
Найпростіший спосіб створити вектор - перелічити значення в квадратних дужках:
julia> num_vec = [1, 4, 9]
3-element Vector{Int64}:
1
4
9
julia> str_vec = ["arrays", "are", "important"]
3-element Vector{String}:
"arrays"
"are"
"important"
Тип вектора відповідає типу кожного елемента.
Технічно це вектор-стовпець, але поки що проігноруймо це, якщо лінійна алгебра нам незнайома. Це стане зрозумілішим після концепції багатовимірних масивів.
Отже, вектори мають бути однорідними (усі елементи одного типу)?
Певною мірою так, і якщо ми хочемо найкращої продуктивності, варто прагнути саме цього.
Утім, Julia може обійти це обмеження (коли потрібно) за допомогою типу Any:
julia> mixed_vector = [1, "str", 'c']
3-element Vector{Any}:
1
"str"
'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
Створюючи порожній вектор, Julia типово використовує тип елементів Any, але його можна задати точніше, позначивши порожній масив потрібним типом елементів.
julia> empty_vec = []
Any[]
julia> empty_int_vec = Int64[]
Int64[]
Дуже часто ми починаємо з векторів, що складаються лише з 0 або лише з 1. Для цього є функції (як не дивно) zeros() та ones(), які приймають розмір вектора як параметр.
Типовий тип - Float64, але за бажанням можна вказати й інші числові типи.
Так само функція fill() приймає і значення для повторення, і бажаний розмір вектора.
julia> zeros(3)
3-element Vector{Float64}:
0.0
0.0
0.0
julia> ones(3)
3-element Vector{Float64}:
1.0
1.0
1.0
julia> ones(Int64, 3)
3-element Vector{Int64}:
1
1
1
julia> fill(42, 3)
3-element Vector{Int64}:
42
42
42
Є багато інших варіантів: про них можна прочитати в посібнику.
Прочитаймо й запамʼятаймо це правило:
Це звично для всіх, хто працював з іншими мовами для аналізу даних (R, Matlab, Fortran...), але може шокувати програмістів із досвідом мов, схожих на C.
Загалом індекси працюють так, як неважко здогадатися: просто беремо їх у квадратні дужки:
squares = [0, 1, 4, 9, 16]
squares[1] # 0
squares[3] # 4
squares[begin] # 0 ("begin" is synonym for 1)
squares[end] # 16 ("end" is synonym for length(squares))
Звернімо увагу, як зручно індексувати за допомогою end (це справді корисно) і begin (а це, мабуть, ні).
Програмісти Python можуть замислитися про відʼємні індекси.
Не варто: вони не є частиною Julia і спричинять BoundsError, як і будь-який індекс менший за 1 або більший за length(squares).
Вектори в Julia змінні: можна змінювати вміст окремих комірок.
julia> vals = [1, 3, 5, 7]
4-element Vector{Int64}:
1
3
5
7
julia> vals[2] = 4 # reassign the value of this element
4
# Only the value at position 2 changes:
julia> vals
4-element Vector{Int64}:
1
4
5
7
Є багато доступних функцій для роботи з векторами, хоча документація Julia узагальнює їх до «колекцій», а не векторів.
Зауважмо, що за домовленістю функції, які змінюють свої вхідні дані, мають ! у назві.
Компілятор цього не вимагає, але у світі Julia це дуже стійка домовленість.
Ось кілька корисних функцій:
push!().pop!().pushfirst!() і popfirst!().insert!() і deleteat!().julia> vals = [1, 3]
2-element Vector{Int64}:
1
3
julia> push!(vals, 5, 6) # can add multiple values
4-element Vector{Int64}:
1
3
5
6
julia> pop!(vals) # mutates vals, return popped value
6
julia> vals
3-element Vector{Int64}:
1
3
5
Конкатенація векторів доволі проста, хоча побіжний погляд у посібник попереджає про складнощі, що чекають на нас із багатовимірними масивами.
Функція append!() може приймати як вхідні дані суміш із кількох векторів та окремих елементів.
julia> append!([1, 2], [3, 4], [-1, -2], 15)
7-element Vector{Int64}:
1
2
3
4
-1
-2
15