Функции - это отдельные фрагменты кода, которые выполняют определенную задачу. Вы даете функции имя, которое определяет, что она делает, и это имя используется для «вызова» функции для выполнения ее задачи, когда это необходимо.

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

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

Определение и вызов функций

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

Каждая функция имеет имя функции , которое описывает задачу, которую выполняет функция. Чтобы использовать функцию, вы «вызываете» эту функцию с ее именем и передаете ей входные значения (известные как аргументы ), которые соответствуют типам параметров функции. Аргументы функции всегда должны указываться в том же порядке, что и список параметров функции.

Вызывается функция в приведенном ниже примере greet(person:), потому что это то, что она делает - она ​​принимает имя человека в качестве ввода и возвращает приветствие для этого человека. Для этого вы определяете один входной параметр - Stringзначение с именем person- и тип возвращаемого значения String, который будет содержать приветствие для этого человека:

func greet(person: String) -> String {

let greeting = "Hello, " + person + "!"

return greeting

}

Вся эта информация свернута в функцию по определению , которое приставкой с funcключевым словом. Тип возврата функции указывается стрелкой возврата ->(дефис, за которым следует правая угловая скобка), за которой следует имя возвращаемого типа.

Определение описывает, что делает функция, что она ожидает получить и что она возвращает, когда это сделано. Определение облегчает однозначный вызов функции из любого места в вашем коде:

print(greet(person: "Anna"))

// Prints "Hello, Anna!"

print(greet(person: "Brian"))

// Prints "Hello, Brian!"

Вы вызываете greet(person:)функцию, передавая ей Stringзначение после personметки аргумента, например . Поскольку функция возвращает значение, ее можно заключить в вызов функции для печати этой строки и просмотра ее возвращаемого значения, как показано выше.greet(person: "Anna")Stringgreet(person:)print(_:separator:terminator:)

ЗАМЕТКА

print(_:separator:terminator:)Функция не имеет метки для первого аргумента, и другие аргументы не являются обязательными , поскольку они имеют значение по умолчанию. Эти вариации на синтаксисе функции рассматриваются ниже в аргументе функции этикетки и Имена параметров и значений параметров по умолчанию .

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

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

Чтобы сделать тело этой функции короче, вы можете объединить создание сообщения и оператор возврата в одну строку:

func greetAgain(person: String) -> String {

return "Hello again, " + person + "!"

}

print(greetAgain(person: "Anna"))

// Prints "Hello again, Anna!"

Параметры функции и возвращаемые значения

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

Функции без параметров

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

func sayHelloWorld() -> String {

return "hello, world"

}

print(sayHelloWorld())

// Prints "hello, world"

Определение функции все еще нуждается в скобках после имени функции, даже если оно не принимает никаких параметров. За именем функции также следует пустая пара скобок при вызове функции.

Функции с несколькими параметрами

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

Эта функция принимает имя человека и его приветствие в качестве входных данных и возвращает соответствующее приветствие для этого человека:

func greet(person: String, alreadyGreeted: Bool) -> String {

if alreadyGreeted {

return greetAgain(person: person)

} else {

return greet(person: person)

}

}

print(greet(person: "Tim", alreadyGreeted: true))

// Prints "Hello again, Tim!"

Вы вызываете greet(person:alreadyGreeted:)функцию, передавая ей Stringзначение аргумента с меткой personи Boolзначение аргумента с меткой alreadyGreetedв скобках, разделенные запятыми. Обратите внимание, что эта функция отличается от greet(person:)функции, показанной в предыдущем разделе. Хотя обе функции имеют имена, начинающиеся с greetgreet(person:alreadyGreeted:)функция принимает два аргумента, но greet(person:)функция принимает только один.

Функции без возвращаемых значений

Функции не обязаны определять тип возвращаемого значения. Вот версия greet(person:)функции, которая печатает собственное Stringзначение, а не возвращает его:

func greet(person: String) {

print("Hello, \(person)!")

}

greet(person: "Dave")

// Prints "Hello, Dave!"

Поскольку не нужно возвращать значение, определение функции не включает стрелку возврата ( ->) или тип возвращаемого значения.

ЗАМЕТКА

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

Возвращаемое значение функции можно игнорировать при ее вызове:

func printAndCount(string: String) -> Int {

print(string)

return string.count

}

func printWithoutCounting(string: String) {

let _ = printAndCount(string: string)

}

printAndCount(string: "hello, world")

// prints "hello, world" and returns a value of 12

printWithoutCounting(string: "hello, world")

// prints "hello, world" but does not return a value

Первая функция, printAndCount(string:)печатает строку, а затем возвращает количество символов в виде Int. Вторая функция printWithoutCounting(string:)вызывает первую функцию, но игнорирует ее возвращаемое значение. Когда вызывается вторая функция, сообщение по-прежнему печатается первой функцией, но возвращаемое значение не используется.

ЗАМЕТКА

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

Функции с несколькими возвращаемыми значениями

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

В приведенном ниже примере определяется вызываемая функция minMax(array:), которая находит самые маленькие и самые большие числа в массиве Intзначений:

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значений.

Поскольку значения членов кортежа названы как часть возвращаемого типа функции, к ним можно получить доступ с точечным синтаксисом, чтобы получить минимальное и максимальное найденные значения:

let bounds = minMax(array: [8, -6, 2, 109, 3, 71])

print("min is \(bounds.min) and max is \(bounds.max)")

// Prints "min is -6 and max is 109"

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

Необязательные типы возврата кортежа

Если тип кортежа, который должен быть возвращен из функции, потенциально может иметь «никакого значения» для всего кортежа, вы можете использовать дополнительный тип возвращаемого кортежа, чтобы отразить тот факт, что может быть весь кортеж nil. Вы пишете необязательный тип возвращаемого кортежа, помещая знак вопроса после закрывающей скобки типа кортежа, например, или .(Int, Int)?(String, Int, Bool)?

ЗАМЕТКА

Необязательный тип кортежа, например, отличается от кортежа, который содержит необязательные типы, такие как . При необязательном типе кортежа необязателен весь кортеж, а не только каждое отдельное значение в кортеже.(Int, Int)?(Int?, Int?)

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

Чтобы безопасно обрабатывать пустой массив, напишите minMax(array:)функцию с необязательным типом возвращаемого кортежа и верните значение, nilкогда массив пуст:

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..<array.count] {

if value < currentMin {

currentMin = value

} else if value > currentMax {

currentMax = value

}

}

return (currentMin, currentMax)

}

Вы можете использовать необязательную привязку, чтобы проверить, minMax(array:)возвращает ли эта версия функции фактическое значение кортежа или nil:

if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {

print("min is \(bounds.min) and max is \(bounds.max)")

}

// Prints "min is -6 and max is 109"

Функции с неявным возвратом

Если все тело функции является одним выражением, функция неявно возвращает это выражение. Например, обе функции ниже имеют одинаковое поведение:

func greeting(for person: String) -> String {

"Hello, " + person + "!"

}

print(greeting(for: "Dave"))

// Prints "Hello, Dave!"



func anotherGreeting(for person: String) -> String {

return "Hello, " + person + "!"

}

print(anotherGreeting(for: "Dave"))

// Prints "Hello, Dave!"

Полное определение greeting(for:)функции - это приветствие, которое она возвращает, что означает, что она может использовать эту более короткую форму. anotherGreeting(for:)Функция возвращает то же самое приветственное сообщение, используя returnключевое слово , как в более функции. Любая функция, которую вы пишете одной returnстрокой, может опустить return.

Ярлыки аргументов функций и имена параметров

Каждый параметр функции имеет как метку аргумента, так и имя параметра . Метка аргумента используется при вызове функции; каждый аргумент записывается в вызове функции с меткой аргумента перед ним. Имя параметра используется при реализации функции. По умолчанию параметры используют свое имя параметра в качестве метки аргумента.

func someFunction(firstParameterName: Int, secondParameterName: Int) {

// In the function body, firstParameterName and secondParameterName

// refer to the argument values for the first and second parameters.

}

someFunction(firstParameterName: 1, secondParameterName: 2)

Все параметры должны иметь уникальные имена. Хотя несколько параметров могут иметь одну и ту же метку аргумента, уникальные метки аргумента помогают сделать ваш код более читабельным.

Указание меток аргумента

Вы пишете метку аргумента перед именем параметра через пробел:

func someFunction(argumentLabel parameterName: Int) {

// In the function body, parameterName refers to the argument value

// for that parameter.

}

Вот вариант greet(person:)функции, которая принимает имя человека и его родной город и возвращает приветствие:

func greet(person: String, from hometown: String) -> String {

return "Hello \(person)! Glad you could visit from \(hometown)."

}

print(greet(person: "Bill", from: "Cupertino"))

// Prints "Hello Bill! Glad you could visit from Cupertino."

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

Пропуск меток аргументов

Если вам не нужна метка аргумента для параметра, напишите underscore ( _) вместо явной метки аргумента для этого параметра.

func someFunction(_ firstParameterName: Int, secondParameterName: Int) {

// In the function body, firstParameterName and secondParameterName

// refer to the argument values for the first and second parameters.

}

someFunction(1, secondParameterName: 2)

Если параметр имеет метку аргумента, аргумент должен быть помечен при вызове функции.

Значения параметров по умолчанию

Вы можете определить значение по умолчанию для любого параметра в функции, назначив значение параметру после типа этого параметра. Если определено значение по умолчанию, вы можете опустить этот параметр при вызове функции.

func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {

// If you omit the second argument when calling this function, then

// the value of parameterWithDefault is 12 inside the function body.

}

someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault is 6

someFunction(parameterWithoutDefault: 4) // parameterWithDefault is 12

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

Вариативные параметры

VARIADIC параметр принимает ноль или более значений определенного типа. Вы используете переменный параметр, чтобы указать, что параметру может быть передано различное количество входных значений при вызове функции. Запишите переменные параметры, вставив три символа точки ( ...) после имени типа параметра.

Значения, передаваемые в переменный параметр, становятся доступными в теле функции в виде массива соответствующего типа. Например, переменный параметр с именем numbersи типом Double...становится доступным в теле функции в виде массива констант, называемого numbersтипом [Double].

В приведенном ниже примере вычисляется среднее арифметическое (также известное как среднее ) для списка чисел любой длины:

func arithmeticMean(_ numbers: Double...) -> Double {

var total: Double = 0

for number in numbers {

total += number

}

return total / Double(numbers.count)

}

arithmeticMean(1, 2, 3, 4, 5)

// returns 3.0, which is the arithmetic mean of these five numbers

arithmeticMean(3, 8.25, 18.75)

// returns 10.0, which is the arithmetic mean of these three numbers

ЗАМЕТКА

Функция может иметь не более одного параметра переменной.

Параметры In-Out

Параметры функции являются константами по умолчанию. Попытка изменить значение параметра функции изнутри тела этой функции приводит к ошибке времени компиляции. Это означает, что вы не можете изменить значение параметра по ошибке. Если вы хотите, чтобы функция изменила значение параметра, и чтобы эти изменения сохранялись после завершения вызова функции, определите этот параметр как параметр in-out .

Вы вводите входной-выходной параметр, помещая inoutключевое слово непосредственно перед типом параметра. Параметр in-out имеет значение, которое передается в функцию, модифицируется функцией и передается обратно из функции для замены исходного значения. Для подробного обсуждения поведения входных-выходных параметров и связанных оптимизаций компилятора см. Входные-выходные параметры .

Вы можете передать только переменную в качестве аргумента для параметра in-out. Вы не можете передать константу или литеральное значение в качестве аргумента, потому что константы и литералы не могут быть изменены. Вы помещаете амперсанд ( &) непосредственно перед именем переменной, когда передаете ее в качестве аргумента параметру in-out, чтобы указать, что она может быть изменена функцией.

ЗАМЕТКА

Входные-выходные параметры не могут иметь значения по умолчанию, а переменные параметры не могут быть помечены как inout.

Вот пример вызванной функции swapTwoInts(_:_:), которая имеет два целочисленных параметра in-out aи b:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {

let temporaryA = a

a = b

b = temporaryA

}

swapTwoInts(_:_:)Функция просто меняет значение bINTO aи значение aINTO b. Функция выполняет этот обмен путем сохранения значения aво временной константе с именем temporaryA, назначая значение bдля a, а затем назначая temporaryAк b.

Вы можете вызвать swapTwoInts(_:_:)функцию с двумя переменными типа, Intчтобы поменять их значения. Обратите внимание, что имена someIntи anotherIntимеют префикс с амперсандом, когда они передаются в swapTwoInts(_:_:)функцию:

var someInt = 3

var anotherInt = 107

swapTwoInts(&someInt, &anotherInt)

print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")

// Prints "someInt is now 107, and anotherInt is now 3"

В приведенном выше примере показано, что исходные значения someIntи anotherIntмодифицируются swapTwoInts(_:_:)функцией, даже если они были изначально определены вне функции.

ЗАМЕТКА

Параметры ввода-вывода не совпадают с возвратом значения из функции. В swapTwoIntsприведенном выше примере не определяется тип возвращаемого значения и не возвращается значение, но он все равно изменяет значения someIntи anotherInt. Параметры in-out - это альтернативный способ воздействия функции за пределы области действия ее функции.

Типы функций

Каждая функция имеет определенный тип функции , состоящий из типов параметров и типа возврата функции.

Например:

func addTwoInts(_ a: Int, _ b: Int) -> Int {

return a + b

}

func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {

return a * b

}

Этот пример определяет две простые математические функции, называемые addTwoIntsи multiplyTwoInts. Каждая из этих функций принимает два Intзначения и возвращает Intзначение, которое является результатом выполнения соответствующей математической операции.

Тип обеих этих функций . Это можно прочитать как:(Int, Int) -> Int

«Функция, которая имеет два параметра, оба типа Int, и которая возвращает значение типа Int».

Вот еще один пример для функции без параметров или возвращаемого значения:

func printHelloWorld() {

print("hello, world")

}

Тип этой функции: «Функция, которая не имеет параметров и возвращает результат ».() -> VoidVoid

Использование типов функций

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

var mathFunction: (Int, Int) -> Int = addTwoInts

Это можно прочитать как:

«Определите переменную по имени mathFunction, которая имеет тип« функция, которая принимает два Intзначения и возвращает Intзначение ». Установите эту новую переменную так, чтобы она ссылалась на вызываемую функцию addTwoInts».

addTwoInts(_:_:)Функция имеет тот же тип, что и mathFunctionпеременная, и поэтому это назначение допускается по типу-проверки Свифта.

Теперь вы можете вызвать назначенную функцию с именем mathFunction:

print("Result: \(mathFunction(2, 3))")

// Prints "Result: 5"

Другой функции с одинаковым типом соответствия можно назначить одну и ту же переменную так же, как и для нефункциональных типов:

mathFunction = multiplyTwoInts

print("Result: \(mathFunction(2, 3))")

// Prints "Result: 6"

Как и с любым другим типом, вы можете оставить его Swift, чтобы выводить тип функции при назначении функции константе или переменной:

let anotherMathFunction = addTwoInts

// anotherMathFunction is inferred to be of type (Int, Int) -> Int

Типы функций как типы параметров

Вы можете использовать тип функции, такой как тип параметра для другой функции. Это позволяет вам оставить некоторые аспекты реализации функции для вызывающей функции, чтобы обеспечить, когда функция вызывается.(Int, Int) -> Int

Вот пример для печати результатов математических функций сверху:

func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {

print("Result: \(mathFunction(a, b))")

}

printMathResult(addTwoInts, 3, 5)

// Prints "Result: 8"

Этот пример определяет вызываемую функцию printMathResult(_:_:_:), которая имеет три параметра. Первый параметр называется mathFunctionи имеет тип . Вы можете передать любую функцию этого типа в качестве аргумента для этого первого параметра. Второй и третий параметры называются и , и оба имеют тип . Они используются как два входных значения для предоставленной математической функции.(Int, Int) -> IntabInt

Когда printMathResult(_:_:_:)вызывается, передается addTwoInts(_:_:)функция, а целочисленные значения 3и 5. Вызывает предоставленную функцию со значениями 3и 5и печатает результат 8.

Роль printMathResult(_:_:_:)заключается в том, чтобы напечатать результат вызова математической функции соответствующего типа. Неважно, что на самом деле делает реализация этой функции - важно только то, что функция имеет правильный тип. Это позволяет printMathResult(_:_:_:)передать некоторые его функции вызывающей функции безопасным для типов способом.

Типы функций как типы возврата

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

В следующем примере определяются две простые функции, которые называются stepForward(_:)и stepBackward(_:)stepForward(_:)Функция возвращает значение больше , чем одна его входное значение, а stepBackward(_:)функция возвращает значение на единицу меньше , чем его входное значение. Обе функции имеют тип :(Int) -> Int

func stepForward(_ input: Int) -> Int {

return input + 1

}

func stepBackward(_ input: Int) -> Int {

return input - 1

}

Вот вызываемая функция chooseStepFunction(backward:), тип возвращаемого значения . Функция возвращает функцию или функцию , основанную на булевом параметре называется :(Int) -> IntchooseStepFunction(backward:)stepForward(_:)stepBackward(_:)backward

func chooseStepFunction(backward: Bool) -> (Int) -> Int {

return backward ? stepBackward : stepForward

}

Теперь вы можете использовать chooseStepFunction(backward:)для получения функции, которая будет двигаться в одном или другом направлении:

var currentValue = 3

let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)

// moveNearerToZero now refers to the stepBackward() function

Приведенный выше пример определяет, нужен ли положительный или отрицательный шаг для перемещения переменной, называемой currentValueпостепенно, ближе к нулю. currentValueимеет начальное значение 3, что означает, что возвращает , в результате чего возвращает функцию. Ссылка на возвращенную функцию хранится в константе с именем .currentValue > 0truechooseStepFunction(backward:)stepBackward(_:)moveNearerToZero

Теперь, когда moveNearerToZeroречь идет о правильной функции, ее можно использовать для отсчета до нуля:

print("Counting to zero:")

// Counting to zero:

while currentValue != 0 {

print("\(currentValue)... ")

currentValue = moveNearerToZero(currentValue)

}

print("zero!")

// 3...

// 2...

// 1...

// zero!

Вложенные функции

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

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

Вы можете переписать chooseStepFunction(backward:)приведенный выше пример для использования и возврата вложенных функций:

func chooseStepFunction(backward: Bool) -> (Int) -> Int {

func stepForward(input: Int) -> Int { return input + 1 }

func stepBackward(input: Int) -> Int { return input - 1 }

return backward ? stepBackward : stepForward

}

var currentValue = -4

let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)

// moveNearerToZero now refers to the nested stepForward() function

while currentValue != 0 {

print("\(currentValue)... ")

currentValue = moveNearerToZero(currentValue)

}

print("zero!")

// -4...

// -3...

// -2...

// -1...

// zero!