1.6. Функции#

Функция в Julia один из основных инструментов повторного использования кода.

Больше информации в мануале [url].

TL;DR

  • Длинная и компактная формы синтаксиса;

  • Анонимные функции;

  • Функция – first-class объект;

  • Принцип передачи аргументов – pass by sharing;

  • Операторы определены через функции;

  • Аргументы и возвращаемое значение определены через кортежи;

  • Аргументы бывают позиционными, по ключу, могут иметь значение по умолчанию.

Основной синтаксис для функций в Julia

julia> function f(x, y)
           return sqrt(x^2 + y^2)
       end
f (generic function with 1 method)

Строго говоря, команда return необязательна: функция в Julia возвращает результат последнего выражения, однако, мы будем придерживаться Code Style: Blue.

В теле функции может находиться несколько команд return, в этом случае при вызове сработает только одна из них. Если необходимо, чтобы функция ничего не возвращала, тогда используется return nothing. Тип Nothing имеет лишь одно значение nothing, представляющее в Julia отутствие значения.

Зачастую пригождается более краткий синтаксис создания функции (assignment form)

julia> f(x, y) = sqrt(x^2 + y^2)
f (generic function with 1 method)

В этом случае справа от = может находиться составное выражение (compound expression) begin-end.

julia> g(x, y) = begin              # или g(x, y) = begin z = f(x, y); return 2*z end
           z = f(x, y)
           return 2*z
       end
g (generic function with 1 method)

Синтаксис вызова интуитивен

julia> f(3, 4)
5.0

julia> g(3, 4)
10.0

В Julia аргументы передаются по принципу pass-by-sharing. Т.е. аргументы функции внутри тела ведут себя как новые переменные. Однако, у изменяемых mutable аргументов (например, массив), можно поменять значения, и они будут видны извне. По соглашению, если функция меняет свой аргумент, то в её имя добавляется ! в конце, например, map!(f, A, B), push!(A, x) (исключение составляют функции, работающие с вводом-выводом print, write,..).

Функции являются first-class объектами. Ими можно «распоряжаться», как переменными: присваивать их другим переменным, передавать как аргумент…

julia> φ = f;

julia> φ(3, 4)
5.0

Важно, что операторы (+-*/, in, ==, &&…) в Julia также являются функциями, просто имя такой функции это символ(ы) оператора

julia> +(1, 2, 3) == 1 + 2 + 3
true

Языковые конструкции, вроде доступа к элементу массива (A[i]) или полю структуры (S.x) являются операторами c функциональными аналогами.

Можно создавать анонимные функции (anonymous functions). Они обычно передаются в другие функции, например, в сортировку или фильтрацию массивов.

julia> x -> 2x                        # короткий синтаксис
#1 (generic function with 1 method)

julia> f4 = x -> 2x                   # анонимная функция x -> 2x присвоена переменной f4
#3 (generic function with 1 method)

julia> f4(8)
16

julia> function (x)                   # длинный синтаксис
           return 3x
       end
#5 (generic function with 1 method)

julia> map(x -> 3x, 1:4)              # пример применения
4-element Vector{Int64}:              # map здесь создает массив
  3                                   # из утроенных значений
  6                                   # арифм. прогрессии от 1 до 4
  9
 12

Аргументы функции в Julia представлены кортежем Tuple. Кортеж это неизменная коллекция, которая может содержать данные разных типов.

julia> tup = (3, 4)
(3, 4)

julia> tup[1]
3

julia> (3,)  # tuple из одного значения
(3,)

Julia позволяет создавать функции с переменным числом аргументов. В таком случае один из аргументов хранит все поданные значения в виде кортежа.

julia> f(x, y...) = @show x y typeof(y)
f (generic function with 2 methods)

julia> f(1, 2, "a")
x = 1
y = (2, "a")
typeof(y) = Tuple{Int64, String}
Tuple{Int64, String}

Функция может возвращать несколько значений. В таком случае возвращается кортеж. Так, функция превращает один кортеж значений (аргументы) в другой (возвращаемое значение).

julia> function addmul(x, y)
           return x + y, x * y   # или return (x + y, x * y)
       end
addmul (generic function with 1 method)

julia> a, b = addmul(3, 4)
(7, 12)

julia> a
7

julia> b
12

Также существует именованный кортеж NamedTuple. В отличие от обычного кортежа, доступ к элементам кортежа можно получить по имени поля.

julia> ntup = (a=10, b="xyz")
(a = 10, b = "xyz")

julia> typeof(ntup)
NamedTuple{(:a, :b), Tuple{Int64, String}}

julia> ntup.a, ntup.b
(10, "xyz")

julia> ntup[1], ntup[2]
(10, "xyz")

julia> ntup[:a], ntup[:b]
(10, "xyz")

На основе NamedTuple создаются функции, принимающие аргументы по ключу. Чтобы отделить позиционные аргументы от аргументов по ключу используется точка с запятой ;. При вызове функции ; можно опускать, но мы не рекомендуем этого делать.

julia> f(; x, y) = x + y
f (generic function with 3 methods)

julia> f(; x=1, y=10)
11

Аргументам (позиционным и по ключу) можно присваивать значения по умолчанию.

julia> f(x; factor=10) = factor * x
f (generic function with 4 methods)

julia> f(15)
150

julia> f(15; factor=2)
30

1.6.1. Упражнения#

function f(x, y = 2, z...; a = 10, b, c...)
    answer = ...
    return answer
end

Ответьте на вопросы про каждый аргумент функции выше

  • Позиционный или по ключу

  • Обязательный при вызове или нет

  • Имеет ли значение по умолчанию

  • Является ли коллекцией аргументов при вызове

Позиционный; обязательный.

Позиционный; необязательный, поскольку имеет значение по умолчанию.

Содержит все позиционные аргументы, за исключением x и y; необязательный.

По ключу; необязательный, поскольку имеет значение по умолчанию.

По ключу; обязательный.

Содержит все аргументы по ключу, за исключением a и b; необязательный.