У концепції Vectors ми казали, що «масиви лежать в основі мови Julia», а вектор є одновимірним.
З огляду на це, цілком розумно сподіватися, що мова надає багато універсальних і потужних способів робити щось з векторами, хоч би що це означало.
Деякі дуже прості функції приймають вектор як вхідні дані, а повертають скалярне значення.
v = [2, 3, 4]
length(v) # => 3
sum(v) # => 9
Припустімо, у нас є числовий вектор, і ми хочемо відняти 0.5 від кожного значення.
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)
Це не спрацьовує, тож що як відняти інший вектор?
julia> v - [0.5, 0.5, 0.5]
3-element Vector{Float64}:
0.7
1.0
1.2
Спрацьовує, але стає досить нудним і вимогливим до памʼяті, коли вектори довшають.
На щастя, у Julia є «магічна» крапка, яка розвʼязує цю проблему дуже просто: достатньо лише v .- 0.5.
Наступний розділ пояснить чому.
Отже, v - 0.5 не спрацьовує, а v .- 0.5 спрацьовує, і нам треба зрозуміти, що робить крапка.
Дві речі, які разом дають бажаний результат.
По-перше, крапка перед будь-яким інфіксним оператором означає «застосувати цю операцію до кожного елемента окремо».
Так само крапка після назви функції «векторизує» її, навіть якщо функцію написано для скалярних вхідних даних.
julia> sqrt.([1, 4, 9])
3-element Vector{Float64}:
1.0
2.0
3.0
У попередньому прикладі ми бачили, що можемо віднімати вектори однакової довжини, але варто розуміти, що .- є безпечнішим оператором, ніж -, бо він ясніше виражає поелементний намір.
julia> v .- [0.5, 0.5, 0.5]
3-element Vector{Float64}:
0.7
1.0
1.2
А що щодо векторів різної довжини?
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
Загалом різна довжина є помилкою, крім випадку, коли один із них має довжину 1 (технічно, «синглтонний» вимір).
Такі синглтони, як [0.5,] або просто 0.5, автоматично розширюються до потрібної довжини повторенням.
Саме в цьому суть broadcasting.
Вибір елементів вектора за номером індексу вже обговорювався в попередніх концепціях.
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']
Також можна вибирати елементи, які задовольняють деякий логічний вираз (технічно, «предикат»). Зазвичай це вимагає 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)
Для складніших виразів крапки зазвичай розмножуються (але вони маленькі й легко набираються).
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)
Нагадаємо, що «вектор» насправді може бути будь-яким відповідним впорядкованим обʼєктом, який можна перебирати, наприклад діапазоном:
julia> n = 3:10
3:10
julia> n[isodd.(n)]
4-element Vector{Int64}:
3
5
7
9
Ми завзяті спостерігачі птахів і ведемо облік того, скільки птахів прилітало до нашого саду за останні сім днів.
На нас чекають шість завдань, і всі вони стосуються кількості птахів, які відвідали наш сад.
Реалізуйте функцію today(), яка повертає кількість птахів, що прилетіли до нашого саду сьогодні.
Підрахунки птахів упорядковані за днями: перший елемент - це підрахунок найдавнішого дня, а останній - сьогоднішній.
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> today(birds_per_day)
1
Реалізуйте функцію increment_todays_count(), щоб збільшити сьогоднішній підрахунок на одиницю:
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> increment_todays_count(birds_per_day)
[2, 5, 0, 7, 4, 2]
Реалізуйте функцію has_day_without_birds(), яка повертає true, якщо був день, коли до нашого саду не прилетів жоден птах; інакше поверніть false:
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> has_day_without_birds(birds_per_day)
true
Реалізуйте функцію count_for_first_days(), яка повертає кількість птахів, що прилетіли до нашого саду від початку тижня, але обмежте підрахунок вказаною кількістю днів від початку тижня.
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> count_for_first_days(birds_per_day, 4)
14
Деякі дні насиченіші за інші.
Насиченим вважається день, коли до нашого саду прилетіло пʼять або більше птахів.
Реалізуйте функцію busy_days(), яка повертає кількість насичених днів:
julia> birds_per_day = [2, 5, 0, 7, 4, 1]
julia> busy_days(birds_per_day)
2
Ми вирішуємо розширити свої записи, зберігаючи підрахунки за кілька тижнів. У кожному випадку підрахунки впорядковані за днями тижня: від понеділка як першого запису до неділі як останнього.
Реалізуйте функцію average_per_day(), яка повертає середнє значення за 2 тижні.
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]
Зареєструйтеся на Exercism, щоб вивчати й опановувати Julia, а також 35 концепцій128 вправ та справжнє наставництво від людей, і все це безкоштовно.