Кожен, хто працював із Python, майже напевно користувався включеннями списків, які є невіддільною частиною синтаксису Python від найперших версій.
Щось настільки зручне поступово потрапляє і в інші мови, зокрема в Julia.
Для програмістів Julia включення радше можливість, ніж необхідність, адже зазвичай є альтернативи (broadcasting, функції вищого порядку тощо).
Однак включення часто дає простий, читабельний і продуктивний спосіб побудувати масив. Зрештою, це справа особистого смаку й того, як ми ставимося до Python порівняно з функціональними мовами.
Синтаксис здебільшого прямо запозичено з Python, але з розширеннями для багатовимірних масивів.
Дуже в дусі Python, включно з необовʼязковою умовою if.
julia> [x^2 for x in 1:3]
3-element Vector{Int64}:
1
4
9
julia> [x^2 for x in 1:3 if isodd(x^2)]
2-element Vector{Int64}:
1
9
Форма вихідних даних залежить від деталей включення: у простих випадках вона зазвичай така сама, як у вхідної колекції, але умова if сплющує її до Vector.
julia> m
2×3 Matrix{Int64}:
1 2 3
4 5 6
julia> [x^2 for x in m]
2×3 Matrix{Int64}:
1 4 9
16 25 36
julia> [x^2 for x in m if isodd(x^2)]
3-element Vector{Int64}:
1
25
9
Як і в Python, ми можемо мати кілька секцій for з різними змінними та однаковими або різними вхідними колекціями.
julia> [x * y for x in 1:3 for y in 4:6]
9-element Vector{Int64}:
4
5
6
8
10
12
12
15
18
julia> [x * y for x in 1:3 for y in 4:6 if isodd(x * y)]
2-element Vector{Int64}:
5
15
Це еквівалентно вкладеним циклам. Вихідні дані одновимірні, навіть якщо на вході матриця.
julia> m
2×3 Matrix{Int64}:
1 2 3
4 5 6
julia> [x*y for x in m for y in m]
36-element Vector{Int64}:
1
4
2
5
(truncated output...)
У попередньому розділі йшлося про кілька секцій for, розділених лише пробілами.
У цьому розділі є лише одна секція for, а змінні розділені комами.
julia> [(x, y) for x in 1:3, y in 4:6]
3×3 Matrix{Tuple{Int64, Int64}}:
(1, 4) (1, 5) (1, 6)
(2, 4) (2, 5) (2, 6)
(3, 4) (3, 5) (3, 6)
Кожна змінна у включенні створює новий вимір у вихідних даних, з елементом для кожної можливої комбінації змінних.
У наведеному вище прикладі x зростає вниз по рядках, а y - упоперек стовпців.
Вищі виміри теж можливі, зі звичними застереженнями щодо читабельності вихідних даних.
У попередніх розділах ішлося про вихідні дані у вигляді масиву, де включення розміщене всередині квадратних дужок [ ... ].
Коли квадратні дужки замінити на круглі ( ... ), відбувається дещо інше: ми отримуємо generator expression, тобто ліниво обчислюваний ітератор, який може видавати наступне значення на вимогу.
julia> g = ((x, y) for x in 1:3, y in 4:6)
Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}, var"#9#10"}(var"#9#10"(), Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}((1:3, 4:6)))
# Indexing fails with a generator, the entries don't exist yet
julia> g[1, 2]
ERROR: MethodError: no method matching getindex(::Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{…}, UnitRange{…}}}, var"#9#10"}, ::Int64, ::Int64)
# conversion to array
julia> collect(g)
3×3 Matrix{Tuple{Int64, Int64}}:
(1, 4) (1, 5) (1, 6)
(2, 4) (2, 5) (2, 6)
(3, 4) (3, 5) (3, 6)
# generators are mainly designed for iteration
julia> [i * j for (i, j) in g]
3×3 Matrix{Int64}:
4 5 6
8 10 12
12 15 18
Коли результат включення одразу використовується для подальшої обробки, генератор може бути ощадливим до памʼяті, уникаючи потреби зберігати великий проміжний масив.
Генератору, використаному як аргумент функції, не потрібні додаткові дужки.
# inefficient
julia> v = [x^2 for x in 1:1e6];
julia> sum(v)
3.333338333335e17
# better - use a generator
julia> sum(x^2 for x in 1:1e6)
3.3333383333312755e17
Синтаксис генератора ідентичний до включень, окрім навколишніх дужок.
Генератори також зручні в конструкторах словників.
julia> Dict(x => x^2 for x in 1:5)
Dict{Int64, Int64} with 5 entries:
5 => 25
4 => 16
2 => 4
3 => 9
1 => 1
Ми працюємо в інтернет-магазині модного одягу. У нас виникає ідея нової можливості сайту: пропонувати користувачам комплекти одягу. Нас цікавлять можливі комплекти, їхні ціни та усунення несумісних варіантів.
Хоча наведені нижче задачі можна розвʼязати різними способами, усі їх можна розвʼязати за допомогою генератора.
Реалізуйте clothingitem(), яка приймає два Tuple: один із categories, а другий із відповідними qualities.
Функція повинна повернути Dict, де ключами є categories, а значеннями - qualities.
julia> categories = ("item_name", "price", "color", "base_color");
julia> qualities = ("Descriptive Name", 99.00, "Ochre Red", "red");
julia> clothingitem(categories, qualities)
Dict{String, Any} with 4 entries:
"price" => 99.0
"item_name" => "Descriptive Name"
"base_color" => "red"
"color" => "Ochre Red"
Функція повинна впоратися з будь-якою кількістю categories.
Реалізуйте get_combinations(), яка приймає Tuple верхів і Tuple низів.
Функція повинна повернути cartesian product цих кортежів як двовимірний масив (тобто Matrix).
Кожен елемент - це комбінація (top, bottom) у вигляді Tuple.
Верх змінюється від рядка до рядка, а низ - від стовпця до стовпця.
julia> tops = (
Dict("item_name" => "Dress shirt"),
Dict("item_name" => "Casual shirt")
);
julia> bottoms = (
Dict("item_name" => "Jeans"),
Dict("item_name" => "Dress trousers")
);
julia> get_combinations(tops, bottoms)
2×2 Matrix{Tuple{Dict{String, String}, Dict{String, String}}}:
(Dict("item_name"=>"Dress shirt"), Dict("item_name"=>"Jeans")) (Dict("item_name"=>"Dress shirt"), Dict("item_name"=>"Dress trousers"))
(Dict("item_name"=>"Casual shirt"), Dict("item_name"=>"Jeans")) (Dict("item_name"=>"Casual shirt"), Dict("item_name"=>"Dress trousers"))
Кожен елемент одягу має повʼязане з ним поле price, тож корисно мати змогу порівнювати повну ціну комплектів.
Реалізуйте get_prices(), яка приймає масив комбінацій одягу.
Функція повинна повернути масив такої самої форми з цінами комплектів на відповідних позиціях.
julia> tops = (
Dict("item_name" => "Dress shirt", "base_color" => "blue", "price" => 35),
Dict("item_name" => "Casual shirt", "base_color" => "black", "price" => 20)
);
julia> bottoms = (
Dict("item_name" => "Jeans", "base_color" => "blue", "price" => 30),
Dict("item_name" => "Dress trousers", "base_color" => "black", "price" => 75)
);
julia> combomatrix = get_combinations(tops, bottoms);
julia> get_prices(combomatrix)
2×2 Matrix{Int}::
65 110
50 95
julia> filteredmatrix = filter_clashing(combomatrix)
1-element Vector{Tuple{Dict{String, Any}, Dict{String, Any}}}:
(Dict("price" => 20, "item_name" => "Casual shirt", "base_color" => "black"), Dict("price" => 30, "item_name" => "Jeans", "base_color" => "blue"))
julia> get_prices(filteredmatrix)
1-element Vector{Int}:
50
Кожен елемент одягу має поле base_color.
Скористайтеся цим полем, щоб прибрати всі комбінації, у яких верх і низ мають однаковий базовий колір.
Реалізуйте filter_clashing(), яка приймає масив комбінацій одягу.
Поверніть одновимірний масив (тобто Vector) відповідних комбінацій.
julia> tops = (
Dict("item_name" => "Dress shirt", "base_color" => "blue", "price" => 35),
Dict("item_name" => "Casual shirt", "base_color" => "black", "price" => 20)
);
julia> bottoms = (
Dict("item_name" => "Jeans", "base_color" => "blue", "price" => 30),
Dict("item_name" => "Dress trousers", "base_color" => "black", "price" => 75)
);
julia> combos = get_combinations(tops, bottoms);
julia> filter_clashing(combos)
1-element Vector{Tuple{Dict{String, Any}, Dict{String, Any}}}:
(Dict("price" => 20, "item_name" => "Casual shirt", "base_color" => "black"), Dict("price" => 30, "item_name" => "Jeans", "base_color" => "blue"))
Зареєструйтеся на Exercism, щоб вивчати й опановувати Julia, а також 35 концепцій128 вправ та справжнє наставництво від людей, і все це безкоштовно.