Основы Swift / 8.1. Функции - Основы


Видео


Функции - основы
В общем виде функция ставит в соответствие элементу одного множества какой-то элемент другого множества.

Функции это самосодержащиеся части кода, который выполняет определённую задачу. Вы даёте имя функции, которое указывает на то, что она делает; и это имя используется для "вызова" функции для выполнения её задачи, когда это необходимо.

Унифицированный синтаксис функций Swift достаточно гибок для выражения любой функции, начина от C-функции без параметров и заканчивая сложным методом Objective-C с именами и ярлыками для каждого параметра. Параметры могут предоставлять значения по-умолчанию для упрощения вызова функции и могут быть переданы в качестве in-out параметров, которые изменяют переданную переменную после завершения функцией своего выполнения.

Каждая функция в Swift имеет тип, основанный на типах параметров функции и её возвращаемом типе. Вы можете использовать такой тип как любой другой в Swift, что делает простым передачу функций в качестве параметров в другие функции, а также возвращать функции из функций. Функции также могут быть записаны внутри других функций для инкапсуляции полезного функционала внутри пространства вложенной функции.
Определение и вызов функций
Когда Вы определяете функцию, Вы можете опционально определить одно или более именованных, типизированных значений, которые функция принимает на входе, известных как параметры. Вы также можете опционально определить тип значения, возвращаемое функцией в качестве выходного, когда она заканчивает своё выполнение, известного как тип возвращаемого значения.

Каждая функция имеет имя функции, которое описывает задачу, которую осуществляет функция. Для использования функции Вы можете "вызвать" эту функцию по её имени, передав в неё входные данные (известные как аргументы), которые совпадают с типами параметров функции. Аргументы функции всегда должны быть предоставлены в том же порядке, что и в списке параметров функции.
@{8.1\1\1}
Функция в примере называется greet(person:), так как то, что она делает - она принимает имя человека в качестве входного значения и возвращает для этого человека приветствие. Для достижения этого Вы задаете один входной параметр - значение типа String под названием person - и выходной имя String, который будет содержать приветствие для этого человека:
func greet(person: String) -> String {
    let greeting = "Здравствуй, " + person + "!"
    return greeting
}
Вся эта информация заключена в определении функции, которое претворяется ключевым словом func. Вы определяете тип возвращаемого функцией значения с помощью стрелки-возврата -> (дефис, дополненный правой угловой скобкой), которое дополняется именем типа возвращаемого.
Определение
Определение описывает, что делает функция, что она ожидает получить и что возвращает по окончании выполнения. Определение помогает функции быть вызванной без неопределённости в любом месте Вашего кода:
@{8.1\1\2}
print(greet(person: "Артём"))
// Печатает "Здравствуй, Артём!"
print(greet(person: "Женя"))
// Печатает "Здравствуй, Женя!"
Вы вызываете функцию greet(person:), передавая ей значение типа String после маркера аргумента label, как например greet(person: "Anna"), так как функция возвращает значение String, то greet(person:) может быть обёрнута в вызов print(_:separator:terminator:) для вывода этой строки и вывода её возвращаемого значения, как указано выше.

Функция print(_:separator:terminator:) не имеет маркера для своего первого аргумента, а все остальные аргументы опциональны, так как имеют стандартное значение.
Упрощение функции
Тело функции greet(person:) начинается с определения новой константы типа String под названием greet и заданием её в простое приветственное сообщение. Это приветствие возвращается назад из функции с использованием ключевого слова return. На строке кода return greet функция завершает своё выполнение и возвращает текущее значение greeting.

Вы можете вызвать функцию greet(person:) несколько раз с разными входными значениями. Пример выше демонстрирует, что будет, если она вызвана со входным значением "Артём" и входным значением "Женя". Функция возвращает персональное приветствие.

Чтобы сделать тело функции короче, Вы можете объединить создание сообщения и интродукцию возврата управления в одной строке.
@{8.1\2}
func greetAgain(person: String) -> String {
    return "Снова здравствуй, " + person + "!"
}
print(greetAgain(person: "Артём"))
// Выведет "Снова здравствуй, Артём!"
Параметры функций и возвращаемые значения
Параметры функций и возвращаемые значения максимально гибки в Swift. Вы можете определить что угодно, начиная от простой функции-инструмента с одним наименованным параметром и до сложной функции с выраженными именами параметров и различными их опциями.

Функциям необязательно иметь входные параметры. Здесь представлена функция без входных параметров, которая всегда возвращает одного и то же сообщение типа String вне зависимости от момента вызова:
@{8.1\3}
func sayHelloWorld() -> String {
    return "я хочу купить подписку на курсы"
}
print(sayHelloWorld())
// Выведет "я хочу купить подписку на курсы"
Определение функции все так же требует круглых скобок после своего имени, даже если функция не принимает никаких параметров. Имя функции так же сопровождается пустой парой круглых скобок, когда она вызывается.
Заметим, что это не противоречит определению функции как соответствию между одним и другим множеством: элементам пустого множества ставится в соответствие элемент типа String.
Функции с несколькими параметрами
Функции могут иметь множеств входных параметров, которые пишутся в круглых скобках функции, отделяясь друг от друга запятыми.

Эта функция принимает имя человека, а также то, были ли они уже поприветствованы, и возвращает подходящее приветствие для этого человека:
@{8.1\4}
func greet(person: String, alreadyGreeted: Bool) -> String {
    if alreadyGreeted {
        return greetAgain(person: person)
    } else {
        return greet(person: person)
    }
}
print(greet(person: "Эльмира", alreadyGreeted: true))
// Выведет "Снова здравствуй, Эльмира!"
Вы вызываете функцию greet(person:alreadyGreeted:), передавая в неё и аргумент типа String с маркером person, и аргумент типа Bool с именем alreadyGreeted в круглых скобках, разделяя их запятыми. Отметьте, что эта функция отличается от функции greet(person:), указанной в примерах ранее. И хотя обе функции имеют название greet, greet(person:alreadyGreeted:) принимает два аргумента, но функция greet(person:) принимает только один.
Функции без возвращаемых значений
Функциям необязательно иметь возвещаемое значение. Здесь представлена версия функции greet(person:), которая печатает само значение String вместо его возврата:
Побочные эффекты функций
Функции можно разделить на 4 типа:
  • Ничего не возвращающие и не оказывающие побочных эффектов
  • Ничего не возвращающие и оказывающие побочные эффекты
  • Возвращающие какое-то значение и не оказывающие побочных эффектов
  • Возвращающие какое-то значение и оказывающие побочные эффекты
Под побочными эффектами понимается воздействие функции на её контекст, то есть на окружающее пространство, которое может выражаться неявно.

Функции без возвращаемых значений могут относиться к первому и второму типам.
@{8.1\5}
func greet(person: String) {
    print("Здравствуй, \(person)!")
}
greet(person: "Катерина")
// Выведет "Здравствуй, Катерина!"
Так как функции не нужно возвещаемое значение, то её определение не содержит возвращающей стрелки (->) или типа возвращаемого значения.

Строго говоря, эта версия функции greet(person:) всё ещё возвращает значение, хотя никакого возвращаемого значения не определено. Функции без определённого возвращаемого значения возвращают специальное значение типа Void. Это просто пустой кортеж, который записывается как ().

Если Вы знакомы с другими языками программирования, то такие функции могут быть известны Вам как процедуры.
Игнорирование возвращаемого значения
Возвращаемое значение функции может быть проигнорировано при её вызове:
@{8.1\6}
func printAndCount(string: String) -> Int {
    print(string)
    return string.characters.count
}
func printWithoutCounting(string: String) {
    let _ = printAndCount(string: string)
}
printAndCount(string: "продли подписку в ближайшее время")
// Выведет "продли подписку в ближайшее время" и вернёт значение 12
printWithoutCounting(string: "продли подписку в ближайшее время")
// Выведет "продли подписку в ближайшее время", но не вернёт значения
Первая функция - printAndCount(string:) - печатает строку и возвращает количество символов в ней в виде значения Int. Вторая функция - printWithoutCounting(string:) вызывает первую функцию, но игнорирует её возвращаемое значение. Когда вызывается вторая функция, то сообщение всё еще печатается первой функцией, но возвращаемое значение используется.

Возвращаемые значения могут быть проигнорированы, но функция, которая утверждает в определении, что имеет возвращаемое значение, должна его иметь. Функция с определённым возвращаемым значением не может позволить управлению выйти за нижнюю границу функции без возвращения значения, а попытка так сделать приведёт к ошибке времени выполнения.
Функции с несколькими возвращаемыми значениями
Вы также можете использовать тип-кортеж в качестве возвращаемого типа функции для возврата нескольких значений в качестве составного.

Пример определяет функцию с названием minMax(array:), которая находит наименьшее и наибольшее числа в массива типа Int:
@{8.1\7\1}
func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..< array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}
Функция minMax(array:) возвращает кортеж, содержащий два значения типа Int. Эти значения помечены как min и max, так что они могут быть использованы с помощью доступ через их имена, когда запрашивается возвращаемое значение функции.

Тело функции minMax(array:) начинается с установки двух рабочих переменных с названиями currentMin и currentMax и значениями в первое целое в массиве. Затем функция итерируется через оставшиеся значения в массиве и проверяет каждое значение, чтобы увидеть, больше или меньше оно, чем значения currentMin и currentMax соответственно. В итоге и максимальное, и минимальное значения возвращаются в качестве кортежа из двух значений Int.
Функции с несколькими возвращаемыми значениями
Так как члены кортежа получили имена в качестве возвращаемого типа функции, то они могут быть использованы после получения доступа с помощью синтаксиса точки для извлечения найденных минимального и максимального значений:

Заметьте, что члены кортежа не обязаны быть проименованы в месте возврата кортежа из функции, так как их имена уже специфицированы как часть возвращаемого функцией типа.
@{8.1\7\2}
let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("минимум равен \(bounds.min) и максимум равен \(bounds.max)")
// Выведет "минимум равен -6 и максимум равен 109"
Опциональный кортеж в качестве возвращаемого значения
Если тип кортежа, который возвращается из функции, потенциально может иметь значение "нет значения" для всего кортежа, то Вы можете использовать опциональный кортеж для возвращаемого типа, чтобы отразить тот факт, что весь кортеж может быть равен nil. Вы можете записать опционал-кортеж в качестве возвращаемого типа, поставив знак вопроса после закрывающей круглой скобки у типа кортежа, например, (Int, Int)? или (String, Int, Bool)?.

Опционал-кортеж, например, (Int, Int)? отличается от кортежа, который содержит значения-опционалы, например, (Int?, Int?). В типе кортеже-опционале весь кортеж есть опционал, а не просто каждое отдельное значение в кортеже.

Функция minMax(array:) выше возвращает кортеж, содержащий два значения типа Int. Однако, функция не выполняет никаких проверок безопасности на полученной массиве. Если аргумент array содержит пустой массив, то функция minMax(array:), описанная выше, выдаст ошибку времени выполнения при попытке получить доступ к array[0].

Чтобы обработать пустой массив безопасно, мы напишем функцию minMax(array:) с кортежем-опционалом в качестве возвращаемого типа, и вернём значение nil, когда массив пуст:
@{8.1\8\1}
func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1.. currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}
Опциональное связывание
Вы можете использовать опциональное связывание для проверки, вернёт ли эта версия функции minMax(array:) действительный кортеж или nil:
@{8.1\8\2}
if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
    print("минимум равен \(bounds.min) и максимум равен \(bounds.max)")
}
// Выведет "минимум равен -6 и максимум равен 109"