Фу

Функції у Julia

2 вправи

Про концепцію Функції

Концепція Basics показала два способи визначити функцію.

Найзагальніший, багаторядковий:

function muladd(x, y, z)
    x * y + z
end

Форма «присвоєння», або «однорядкова», для коротких означень:

muladd(x, y, z) = x * y + z

У третій, ще коротшій формі можна створити коротку функцію для одноразового використання без імені:

julia> map(x -> 2x, 1:3)
4-element Vector{Int64}:
 2
 4
 6

julia> map((x, y) -> x * y, 1:3, 4:6)
3-element Vector{Int64}:
  4
 10
 18

У цьому випадку x -> 2x - це «анонімна функція». Це те саме, що в деяких інших мовах називають «лямбда-функцією».

Зауважмо, що кілька аргументів потребують дужок, як у (x, y) -> x * y.

Анонімні функції часто трапляються в коді Julia, особливо в поєднанні з функціями вищого порядку, такими як map() і filter().

Аргументи функції

Досі в силабусі ми розглядали лише функції з точною кількістю аргументів, від виклику яких вимагається передати їх усі, і в правильному порядку. Це було б обмежувально й незручно, тому існує кілька інших варіантів.

Необовʼязкові аргументи

Як і багато мов, Julia дозволяє в означеннях функцій задавати типові значення для окремих аргументів.

Тоді виклик функції може або передати значення для такого аргументу, або пропустити його й покластися на типове.

julia> f(x, y=10) = x * y
f (generic function with 2 methods)

julia> f(2, 3)
6

julia> f(2)
20

Усі аргументи без типових значень мають іти перед аргументами з типовими значеннями, тобто f(x=2, y) було б некоректним.

Ключові аргументи

У всіх прикладах досі використано positional arguments, де значення, передані у виклику функції, мають збігатися з порядком відповідних аргументів в означенні функції.

Як і багато мов, Julia також дозволяє keyword arguments. У виклику функції потрібно вказати імʼя аргументу, але кілька ключових аргументів тоді можна задати в будь-якому порядку.

Характерна особливість Julia полягає в тому, що ключові аргументи (якщо вони є) в означенні функції мають іти після крапки з комою ;, яка відділяє їх від позиційних аргументів. А у виклику функції між останнім позиційним і першим ключовим аргументом можна ставити як ;, так і ,.

julia> b(x; y) = x + y
b (generic function with 1 method)

julia> b(2, y=3)
5

# keyword is required when calling
julia> b(2, 3)
ERROR: MethodError: no method matching b(::Int64, ::Int64)
The function `b` exists, but no method is defined for this combination of argument types.

Типові значення можна, за бажанням, задати точно так само, як для позиційних аргументів.

Часто виникає синтаксис на кшталт myarg=myarg у виклику функції, коли змінну з тим самим іменем, що й параметр, було обчислено заздалегідь. У такій ситуації дозволено скорочений синтаксис:

julia> width = 4.0
4.0

julia> height = √ width
2.0

julia> area(; width, height) = width * height
area (generic function with 1 method)

# repetition
julia> area(; width=width, height=height)
8.0

# shorthand form
julia> area(; width, height)
8.0

Splat і slurp

Це стандартні назви для однієї корисної особливості синтаксису Julia, якщо раптом було цікаво. Обидві стосуються оператора ....

Splat

Splat застосовують у викликах функцій, щоб розгорнути колекції в окремі значення, потрібні функції.

Це легше показати, ніж пояснити:

julia> fxyz(x, y, z) = x * y * z
fxyz (generic function with 1 method)

julia> xyz = [2, 3, 4]
3-element Vector{Int64}:
 2
 3
 4

# Using the vector directly in a function call is invalid
julia> fxyz(xyz)
ERROR: MethodError: no method matching fxyz(::Vector{Int64})
The function `fxyz` exists, but no method is defined for this combination of argument types.

# splatting converts the vector to 3 numbers, used as positional arguments
julia> fxyz(xyz...)
24

Деякі «виклики функцій» приховані синтаксичним цукром, тож splat можна застосувати й у менш очевидних місцях.

Наприклад, множинне присвоєння всередині використовує функцію-конструктор кортежу:

julia> first, rest... = [1, 2, 3, 4]
4-element Vector{Int64}:
 1
 2
 3
 4

julia> first
1

julia> rest
3-element Vector{Int64}:
 2
 3
 4

Ключові аргументи теж можна передати через splat, зазвичай використовуючи named tuple. Dict також підійде, але ключі мають бути символами: рядок тексту (англ. string) тут не спрацює.

# function with 3 keyword arguments
julia> fabc(; a, b, c) = a + b + c
fabc (generic function with 1 method)

# named tuple
julia> abc_nt = (a=2, b=3, c=4)
(a = 2, b = 3, c = 4)

# there are no positional arguments, so need to use ; before kw argument
julia> fabc(;abc_nt...)
9

# Dict
julia> abc_dict = Dict(:a=>2, :b=>3, :c=>4)
Dict{Symbol, Int64} with 3 entries:
  :a => 2
  :b => 3
  :c => 4

julia> fabc(;abc_dict...)
9
Slurp

Slurp застосовують в означенні функції, щоб запакувати довільну кількість окремих значень у колекцію.

julia> f_more(i, j, more...) = i + j + sum(more)
f_more (generic function with 1 method)

julia> f_more(1, 3, 5, 7, 9, 11)
36

Імʼя аргументу, який збирається через slurp (у цьому випадку more), не має значення. Тип цієї змінної обирає компілятор, але для позиційних аргументів він, найімовірніше, буде tuple або чимось подібним.

Ключові аргументи теж можна зібрати через slurp, отримавши Dict (або подібне).

julia> f_kwslurp(x, y; switches...) = :mult in keys(switches) ? x * y : x + y
f_kwslurp (generic function with 1 method)

julia> f_kwslurp(5, 6; mult=true)
30

julia> f_kwslurp(5, 6)
11

У виклику можна використати будь-які ключові аргументи. Саме означення функції вирішує, на які ключі реагувати, а які ігнорувати.

Редагувати через GitHub Посилання відкривається в новому вікні або вкладці

Вивчити концепцію Функції

Практика заблокована

Розблокуйте ще 1 вправу, щоб практикувати концепцію Функції