Ве

Вектори у Julia

1 вправа

Про концепцію Вектори

Одна з головних цілей 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

Є багато інших варіантів: про них можна прочитати в посібнику.

Прочитаймо й запамʼятаймо це правило:

  • Типово індексація в Julia починається з 1, а не з 0.

Це звично для всіх, хто працював з іншими мовами для аналізу даних (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
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці

Вивчити концепцію Вектори