Функции — SwiftBook
Функции – это самостоятельные фрагменты кода, решающие определенную задачу. Каждой функции присваивается уникальное имя, по которому ее можно идентифицировать и «вызвать» в нужный момент.
Язык 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»). Поскольку функция возвращает значение типа String, вызов функции greet(person:) может быть завернут в вызов для функции print(_:separator:terminator:), чтобы напечатать полученную строку и увидеть возвращаемое значение (см. выше).
Тело функции greet(person:) начинается с объявления новой константы типа String под названием greeting, и устанавливается простое сообщение-приветствие. Затем это приветствие возвращается в точку вызова функции с помощью ключевого слова return. После выполнения оператора return greeting функция завершает свою работу и возвращает текущее значение greeting.
Функцию greet(person:) можно вызывать многократно и с разными входными значениями. В примере выше показано, что будет, если функцию вызвать с аргументом «Anna» и со значением «Brian». В каждом случае функция возвратит персональное приветствие.
Чтобы упростить код этой функции, можно записать создание сообщения и его возврат в одну строку:
func greetAgain(person: String) -> String { return "Hello again, " + person + "!" } print(greetAgain(person: "Anna")) // Выведет "Hello again, Anna!"
В языке Swift параметры функций и возвращаемые значения реализованы очень гибко. Разработчик может объявлять любые функции – от простейших, с одним безымянным параметром, до сложных, со множеством параметров и составными именами.
Функции без параметров
В некоторых случаях функции могут не иметь входных параметров. Вот пример функции без входных параметров, которая при вызове всегда возвращает одно и то же значение типа String:
func sayHelloWorld() -> String {
return "hello, world"
}
print(sayHelloWorld())
// Выведет "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))
// Выведет "Hello again, Tim!"
Вы вызываете функцию greet(person:alreadyGreeted:), передавая значение типа String параметру с ярлыком person и булево значение с ярлыком alreadyGreeted, взятое в скобки через запятую. Обратите внимание, что эта функция отличается от функции greet(person:), которую вы видели в предыдущем разделе. Хотя имена обеих функций начинаются с greet, функция greet(person:alreadyGreeted:) принимает два аргумента, а greet(person:) принимает только один.
Функции, не возвращающие значения
В некоторых случаях функции могут не иметь возвращаемого типа. Вот другая реализация функции greet(person:), которая выводит свое собственное значение типа String, но не возвращает его:
func greet(person: String) { print("Hello, \(person)!") } greet(person: "Dave") // Выведет "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") // Выведет "hello, world" и возвращает значение 12 printWithoutCounting(string: "hello, world") // Выведет "hello, world", но не возвращает значения
Первая функция, 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)")
// Выведет "min is -6 and max is 109"
Обратите внимание, что элементам кортежа не нужно давать название в момент возвращения кортежа из функции, так как их имена уже указаны как часть возвращаемого типа функции.
Опциональный кортеж как возвращаемый тип
Если возвращаемый из функции кортеж может иметь «пустое значение», то его следует объявить как опциональный кортеж, т. е. кортеж, который может равняться nil. Чтобы сделать возвращаемый кортеж опциональным, нужно поставить вопросительный знак после закрывающей скобки:(Int, Int)? или (String, Int, Bool)?.
Заметка
Кортеж-опционал вида (Int, Int)? это не то же самое, что кортеж, содержащий опционалы: (Int?, Int?). Кортеж-опционал сам является опционалом, но не обязан состоять из опциональных значений.
Функция minMax(array:) выше возвращает кортеж из двух значений типа Int, однако не проверяет корректность передаваемого массива. Если аргумент array содержит пустой массив, для которого count равно 0, функция minMax в том виде, в каком она приведена выше, выдаст ошибку выполнения, когда попытается обратиться к элементу 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)")
}
// Выведет "min is -6 and max is 109"
Каждый параметр функции имеет ярлык аргумента и имя параметра. Ярлык аргумента используется при вызове функции. Каждый параметр при вызове функции записывается с ярлыком аргумента, стоящим перед ним. Имя параметра используется при реализации функции. По умолчанию параметры используют имена их параметров в качестве ярлыка аргумента.
func someFunction(firstParameterName: Int, secondParameterName: Int) {
// Внутри тела функции firstParameterName и secondParameterName
// ссылаются на значения аргументов, первого и второго параметров.
}
someFunction(firstParameterName: 1, secondParameterName: 2)
Все параметры должны иметь уникальные имена. Несмотря на то, что несколько параметров могут иметь один ярлык аргумента, уникальные ярлыки аргумента метки помогают сделать ваш код более читабельным.
Указываем ярлыки аргументов
Вы пишите ярлык аргумента перед именем параметра через пробел:
func someFunction(argumentLabel parameterName: Int) {
// В теле функции parameterName относится к значению аргумента
// для этого параметра.
}
Вот вариант функции 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"))
// Выводит "Hello Bill! Glad you could visit from Cupertino."
Использование ярлыков аргументов позволяет функции вызываться в более выразительной манере, в виде предложения, при этом все же предоставляя тело функции в более читаемом виде и с более понятыми намерениями.
Пропуск ярлыков аргумента
Если вы не хотите использовать ярлык аргумента в качестве параметра, используйте подчеркивание (_) вместо явного ярлыка аргумента для этого параметра.
func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
// В теле функции firstParameterName и secondParameterName
// ссылаются на значения аргументов для первого и второго параметров.
}
someFunction(1, secondParameterName: 2)
Если у параметра есть ярлык аргумента, то аргумент должен иметь ярлык при вызове функции.
Значения по умолчанию для параметров
При объявлении функции любому из ее параметров можно присвоить значение по умолчанию. Если у параметра есть значение по умолчанию, то при вызове функции этот параметр можно опустить.
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
// Если вы пропускаете второй аргумент при вызове функции, то
// значение parameterWithDefault будет равняться 12 внутри тела функции.
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault равен 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault равен 12
Расположите параметры, у которых нет дефолтных значений в начале списка параметров функции до параметров с дефолтными значениями. Параметры, не имеющие значения по умолчанию, как правило, более важны для значения функции — их запись в первую очередь облегчает разпознавание функции уже вызванной ранее, независимо от того, опущены ли какие-то параметры по умолчанию.
Вариативные параметры
Вариативным называют параметр, который может иметь сразу несколько значений или не иметь ни одного. С помощью вариативного параметра можно передать в функцию произвольное число входных значений. Чтобы объявить параметр как вариативный, нужно поставить три точки (…) после его типа.
Значения, переданные через вариативный параметр, доступны внутри функции в виде массива соответствующего типа. Например, вариативный параметр 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)
// возвращает 3.0, что является средним арифметическим этих пяти чисел
arithmeticMean(3, 8.25, 18.75)
// возвращает 10.0, что является средним арифметическим этих трех чисел
Заметка
У функции может быть только один вариативный параметр.
Сквозные параметры
Параметры функции по умолчанию являются константами. Попытка изменить значение параметра функции из тела этой функции приводит к ошибке компиляции. Это означает, что вы не сможете изменить значение параметра по ошибке. Если вы хотите, чтобы функция изменила значение параметра, и вы хотите, чтобы эти изменения сохранились после того, как закончился вызов функции, определите этот параметр в качестве сквозного параметра.
Для создания сквозного параметра нужно поставить ключевое слово inout перед объявлением параметра. Сквозной параметр передает значение в функцию, которое затем изменяется в ней и возвращается из функции, заменяя исходное значение. Более подробную информацию поведения сквозных параметров и связанных с ними оптимизаций компилятора см. Сквозные Параметры.
Вы можете передать только переменную в качестве аргумента для сквозного параметра. Вы не можете передать константу или значения литерала в качестве аргумента, так как константы и литералы не могут быть изменены. Вы ставите амперсанд (&) непосредственно перед именем переменной, когда передаете ее в качестве аргумента сквозного параметра, чтобы указать, что он может быть изменен с помощью функции.
Заметка
Сквозные параметры не могут иметь значения по умолчанию, а вариативные параметры не могут быть сквозными, с ключевым словом inout.
Вот пример функции под названием swapTwoInts(_:_:), у которой есть два сквозных целочисленных параметра – a и b:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
Функция swapTwoInts(_:_:) просто меняет значение переменной b на значение a, а значение a – на значение 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)")
// Выведет "someInt is now 107, and anotherInt is now 3"
В вышеприведенном примере видно, что исходные значения переменных someInt и anotherInt изменены функцией swapTwoInts (_: _:), несмотря на то, что изначально они были объявлены за ее пределами.
Заметка
Сквозные параметры – это не то же самое, что возвращаемые функцией значения. В примере с функцией swapTwoInts нет ни возвращаемого типа, ни возвращаемого значения, но параметры someInt и anotherInt все равно изменяются. Сквозные параметры – это альтернативный способ передачи изменений, сделанных внутри функции, за пределы тела этой функции.
У каждой функции есть специальный функциональный тип, состоящий из типов параметров и типа возвращаемого значения.
Пример:
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")
}
Эта функция имеет тип () -> Void, т. е. «функция без параметров, которая возвращает Void».
Использование функциональных типов
В Swift с функциональными типами можно работать так же, как и с другими типами. Например, можно объявить константу или переменную функционального типа и присвоить ей функцию соответствующего типа:
var mathFunction: (Int, Int) -> Int = addTwoInts
Эта запись означает следующее:
«Объявить переменную mathFunction, имеющую тип «функция, принимающая два значения типа Int, и возвращающая одно значение типа Int». Присвоить этой новой переменной указатель на функцию addTwoInts».
Функция addTwoInts имеет тот же тип, что и переменная mathFunction, поэтому с точки зрения языка Swift такое присваивание корректно.
Теперь функцию можно вызывать с помощью переменной mathFunction:
print("Result: \(mathFunction(2, 3))")
// Выведет "Result: 5"
Той же переменной можно присвоить и другую функцию такого же типа – аналогично нефункциональным типам:
mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))")
// Выведет "Result: 6"
Как и в случае с любым другим типом, вы можете не указывать тип явно, а предоставить Swift самостоятельно вывести функциональный тип при присваивании функции константе или переменной:
let anotherMathFunction = addTwoInts
// для константы anotherMathFunction выведен тип (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)
// Выведет "Result: 8"
В этом примере объявлена функция printMathResult(_:_:_:), у которой есть три параметра. Первый параметр под названием mathFunction имеет тип (Int, Int) -> Int. Соответственно, аргументом этого параметра может быть любая функция такого же типа. Второй и третий параметры называются a и b и относятся к типу Int. Они служат для передачи двух входных значений для математической функции.
При вызове 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) -> Int. Функция chooseStepFunction(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 > 0 даст true, а функция chooseStepFunction(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 теперь ссылается на вложенную функцию stepForward()
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!
Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.
Swift | Функции
Функции
Последнее обновление: 31.12.2017
Функция представляет набор инструкций, который имеет имя (имя функции) и может использоваться повторно в различных местах программы. Функция имеет следующее формальное определение:
func имя_функции (параметры) -> тип_возвращаемого_значения { // набор инструкций }
Сначала идет ключевое слово func, после которого идет имя функции. Для именования функции применяются в принципе те же правила, что и при именовании переменных. Для имени функции используется режим camel case.
После имени функции в скобках указываются параметры. Если параметров нет, то просто идут пустые скобки.
Если функция возвращает какое-либо значение, то после параметров в скобках идет стрелка и тип возвращаемого значения. И в конце в фигурных скобках собственно идет блок кода, который и представляет функцию.
Определим простейшую функцию:
func printName(){ print("Меня зовут Том") }
Здесь функция называется printName
. Эта функция ничего не возвращает, поэтому тут после скобок сразу идут фигурные скобки с набором операторов.
Данная функция просто выводит строку «Меня зовут Том».
Далее по имени функции мы можем ее вызвать многократно. Для вызова функции указывается ее имя, после которого в скобках перечисляются значения для ее параметров:
func printName(){ print("Меня зовут Том") } printName() printName() printName()
В частности, здесь функция вызывается три раза.
Параметры функции
Теперь используем параметры:
func printInfo(name: String, age: Int){ print("Имя: \(name) ; возраст: \(age)") } printInfo(name: "Tom", age: 18) // Имя: Tom ; возраст: 18 printInfo(name: "Bob", age: 35) // Имя: Bob ; возраст: 35
Количество параметров может быть произвольным. В данном случае мы используем два параметра: для передачи имени и возраста. Для каждого параметра
определено имя и тип. Например, первый параметр называется name
и имеет тип String.
При вызове функции нам надо учитывать имя и тип параметров. При вызове функции ей необходимо передать значения для всех
ее параметров по имени. То есть указывается имя параметра и через двоеточие его значение: name: "Tom"
,
причем передаваемое значение должно соответствовать параметру по типу:
printInfo(name: "Tom", age: 18)
Хотя мы можем указать, что при вызове функции не надо указывать имена параметров при ее вызове. Для этого перед именем параметра нужно поставить подчеркивание. Между подчеркиванием и названием параметра должен идти пробел:
func printInfo(_ name: String, _ age: Int){ print("Имя: \(name) ; возраст: \(age)") } printInfo("Tom", 18) // Имя: Tom ; возраст: 18
При вызове подобной функции значения параметрам передаются по позиции. При этом опять же передаваемое значение должно соответствовать параметру по типу.
Значения параметров по умолчанию
Если мы определили два параметра в функции, то соответственно при ее вызове мы также должны передать ей два значения для параметров. Однако мы также можем установить для параметров значения по умолчанию:
func printInfo(name: String = "Tom", age: Int = 22){ print("Имя: \(name) ; возраст: \(age)") } printInfo(name: "Bob", age: 18) // Имя: Bob ; возраст: 18 printInfo(name: "Alice") // Имя: Alice ; возраст: 22 printInfo() // Имя: Tom ; возраст: 22
И если при вызове функции мы не передадим для какого-то параметра значение, то этот параметр будет использовать значение по умолчанию.
Swift — Функции
Уважаемый пользователь! Реклама помогает поддерживать и развивать наш проект, делая его простым и удобным специально для Вас. Если проект интересный и важный для Вас, то отключите на нем блокировщик рекламы. Спасибо, что читаете сайт!
Функции — это отдельные фрагменты кода, которые выполняют определенную задачу. Вы даете функции имя, которое определяет, что она делает, и это имя используется для «вызова» функции для выполнения ее задачи, когда это необходимо.
Синтаксис унифицированной функции 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")
String
greet(person:)
print(_:separator:terminator:)
ЗАМЕТКА
print(_:separator:terminator:)
Функция не имеет метки для первого аргумента, и другие аргументы не являются обязательными , поскольку они имеют значение по умолчанию. Эти вариации на синтаксисе функции рассматриваются ниже в аргументе функции этикетки и Имена параметров и значений параметров по умолчанию .
Тело greet(person:)
функции начинается с определения новой String
вызываемой константы greeting
и установки ее в простое приветственное сообщение. Это приветствие затем передается обратно из функции с помощью return
ключевого слова. В строке кода, в которой говорится , что функция завершает свое выполнение и возвращает текущее значение .return greeting
greeting
Вы можете вызывать 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:)
функции, показанной в предыдущем разделе. Хотя обе функции имеют имена, начинающиеся с greet
, greet(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(_:_:)
Функция просто меняет значение b
INTO a
и значение a
INTO 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")
}
Тип этой функции: «Функция, которая не имеет параметров и возвращает результат ».() -> Void
Void
Использование типов функций
Вы используете типы функций, как и любые другие типы в 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) -> Int
a
b
Int
Когда 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) -> Int
chooseStepFunction(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 > 0
true
chooseStepFunction(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!
Уважаемый пользователь! Реклама помогает поддерживать и развивать наш проект, делая его простым и удобным специально для Вас. Если проект интересный и важный для Вас, то отключите на нем блокировщик рекламы. Спасибо, что читаете сайт!
Swift — Функции — CoderLessons.com
Функция – это набор операторов, организованных вместе для выполнения определенной задачи. Функция Swift 4 может быть такой же простой, как простая функция C, и такой же сложной, как функция языка Objective C. Это позволяет нам передавать локальные и глобальные значения параметров внутри вызовов функций.
Объявление функции – сообщает компилятору имя функции, тип возвращаемого значения и параметры.
Определение функции – предоставляет фактическое тело функции.
Объявление функции – сообщает компилятору имя функции, тип возвращаемого значения и параметры.
Определение функции – предоставляет фактическое тело функции.
Функции Swift 4 содержат тип параметра и его возвращаемые типы.
Определение функции
В Swift 4 функция определяется ключевым словом «func». Когда функция вновь определена, она может принять одно или несколько значений в качестве входных «параметров» для функции, и она обработает функции в основном теле и передаст значения в функции в качестве выходных «возвращаемых типов».
Каждая функция имеет имя функции, которое описывает задачу, которую выполняет функция. Чтобы использовать функцию, вы «вызываете» эту функцию с ее именем и передаете входные значения (известные как аргументы), которые соответствуют типам параметров функции. Параметры функции также называются «кортежами».
Аргументы функции должны всегда указываться в том же порядке, что и список параметров функции, а за возвращаемыми значениями следует →.
Синтаксис
func funcname(Parameters) -> returntype { Statement1 Statement2 --- Statement N return parameters }
Посмотрите на следующий код. Имя студента объявляется как строковый тип данных, объявленный внутри функции ‘student’, и когда функция вызывается, она возвращает имя студента.
Live Demo
func student(name: String) -> String { return name } print(student(name: "First Program")) print(student(name: "About Functions"))
Когда мы запускаем вышеуказанную программу, используя площадку, мы получаем следующий результат –
First Program About Functions
Вызов функции
Давайте предположим, что мы определили функцию с именем display для рассмотрения. Например, для отображения чисел функция с именем функции display отображается сначала с аргументом no1, который содержит целочисленный тип данных. Затем аргумент ‘no1’ присваивается аргументу ‘a’, который в дальнейшем будет указывать на тот же тип данных integer. Теперь аргумент «а» возвращается в функцию. Здесь функция display () будет содержать целочисленное значение и возвращать целочисленные значения при каждом вызове функции.
Live Demo
func display(no1: Int) -> Int { let a = no1 return a } print(display(no1: 100)) print(display(no1: 200))
Когда мы запускаем программу выше, используя площадку, мы получаем следующий результат –
100 200
Параметры и возвращаемые значения
Swift 4 предоставляет гибкие параметры функции и ее возвращаемые значения от простых до сложных значений. Как и в C и Objective C, функции в Swift 4 также могут принимать несколько форм.
Функции с параметрами
Доступ к функции осуществляется путем передачи значений ее параметров в тело функции. Мы можем передать одно или несколько значений параметров в виде кортежей внутри функции.
Live Demo
func mult(no1: Int, no2: Int) -> Int { return no1*no2 } print(mult(no1: 2, no2: 20)) print(mult(no1: 3, no2: 15)) print(mult(no1: 4, no2: 30))
Когда мы запускаем программу выше, используя площадку, мы получаем следующий результат –
40 45 120
Функции без параметров
У нас также могут быть функции без каких-либо параметров.
Синтаксис
func funcname() -> datatype { return datatype }
Ниже приведен пример с функцией без параметра:
Live Demo
func votersname() -> String { return "Alice" } print(votersname())
Когда мы запускаем вышеуказанную программу, используя площадку, мы получаем следующий результат –
Alice
Функции с возвращаемыми значениями
Функции также используются для возврата значений типа string, integer и float в качестве типов возврата. Чтобы узнать наибольшее и наименьшее число в данном массиве, функция ‘ls’ объявляется с большими и маленькими целочисленными типами данных.
Массив инициализируется для хранения целочисленных значений. Затем массив обрабатывается и каждое значение в массиве читается и сравнивается для его предыдущего значения. Когда значение меньше предыдущего, оно сохраняется в аргументе ‘small’, в противном случае оно сохраняется в аргументе ‘large’, а значения возвращаются при вызове функции.
Live Demo
func ls(array: [Int]) -> (large: Int, small: Int) { var lar = array[0] var sma = array[0] for i in array[1..<array.count] { if i < sma { sma = i } else if i > lar { lar = i } } return (lar, sma) } let num = ls(array: [40,12,-5,78,98]) print("Largest number is: \(num.large) and smallest number is: \(num.small)")
Когда мы запускаем вышеуказанную программу, используя площадку, мы получаем следующий результат –
Largest number is: 98 and smallest number is: -5
Функции без возвращаемых значений
Некоторые функции могут иметь аргументы, объявленные внутри функции без каких-либо возвращаемых значений. Следующая программа объявляет a и b в качестве аргументов функции sum (). внутри самой функции значения аргументов a и b передаются путем вызова вызова функции sum (), и ее значения печатаются, тем самым исключая возвращаемые значения.
Live Demo
func sum(a: Int, b: Int) { let a = a + b let b = a - b print(a, b) } sum(a: 20, b: 10) sum(a: 40, b: 10) sum(a: 24, b: 6)
Когда мы запускаем вышеуказанную программу, используя площадку, мы получаем следующий результат –
30 20 50 40 30 24
Функции с необязательными типами возврата
В Swift 4 введена «дополнительная» функция, позволяющая избавиться от проблем путем введения мер безопасности. Рассмотрим, к примеру, мы объявляем тип возвращаемого значения функции как целое число, но что произойдет, когда функция вернет строковое значение или нулевое значение. В этом случае компилятор вернет значение ошибки. «по желанию» введены, чтобы избавиться от этих проблем.
Необязательные функции могут принимать две формы: «значение» и «ноль». Мы упомянем «Необязательные» с зарезервированным символом «?» проверить, возвращает ли кортеж значение или нулевое значение.
Live Demo
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) } if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) { print("min is \(bounds.min) and max is \(bounds.max)") }
Когда мы запускаем программу выше, используя площадку, мы получаем следующий результат –
min is -6 and max is 109
«Необязательные» используются для проверки «nil» или значений мусора, что отнимает много времени на отладку и делает код эффективным и читаемым для пользователя.
Локальные функции против внешних имен параметров
Имена локальных параметров
Локальные имена параметров доступны только внутри функции.
func sample(number: Int) { print(number) }
Здесь номер аргумента семпла func объявлен как внутренняя переменная, так как он доступен внутри функции sample (). Здесь ‘число’ объявлено как локальная переменная, но ссылка на переменную делается вне функции с помощью следующего оператора:
Live Demo
func sample(number: Int) { print(number) } sample(number: 1) sample(number: 2) sample(number: 3)
Когда мы запускаем вышеуказанную программу, используя площадку, мы получаем следующий результат –
1 2 3
Имена внешних параметров
Имена внешних параметров позволяют нам называть параметры функции, чтобы сделать их назначение более понятным. Например, ниже вы можете назвать два параметра функции и затем вызвать эту функцию следующим образом:
Live Demo
func pow(firstArg a: Int, secondArg b: Int) -> Int { var res = a for _ in 1..<b { res = res * a } print(res) return res } pow(firstArg:5, secondArg:3)
Когда мы запускаем вышеуказанную программу, используя площадку, мы получаем следующий результат –
125
Вариативные параметры
Когда мы хотим определить функцию с множественным числом аргументов, мы можем объявить членов как «переменные» параметры. Параметры могут быть указаны как переменные с помощью (···) после имени параметра.
Live Demo
func vari<N>(members: N...){ for i in members { print(i) } } vari(members: 4,3,5) vari(members: 4.5, 3.1, 5.6) vari(members: "Swift 4", "Enumerations", "Closures")
Когда мы запускаем вышеуказанную программу, используя площадку, мы получаем следующий результат –
4 3 5 4.5 3.1 5.6 Swift 4 Enumerations Closures
Константа, переменная и параметры ввода / вывода
Функции по умолчанию рассматривают параметры как «константы», тогда как пользователь может объявлять аргументы функций также как переменные. Мы уже обсуждали, что ключевое слово let используется для объявления постоянных параметров, а переменные параметры определяются с помощью ключевого слова var.
Параметры ввода / вывода в Swift 4 обеспечивают функциональность для сохранения значений параметров, даже если их значения изменяются после вызова функции. В начале определения параметра функции объявляется ключевое слово «inout» для сохранения значений элементов.
Он получает ключевое слово «inout», поскольку его значения передаются «in» в функцию, а его значения доступны и изменяются ее телом функции, и он возвращается «out» из функции, чтобы изменить исходный аргумент.
Переменные передаются только в качестве аргумента для параметра in-out, поскольку только его значения изменяются внутри и снаружи функции. Следовательно, нет необходимости объявлять строки и литералы в качестве входных параметров. ‘&’ перед именем переменной означает, что мы передаем аргумент параметру in-out.
Live Demo
func temp(a1: inout Int, b1: inout Int) { let t = a1 a1 = b1 b1 = t } var no = 2 var co = 10 temp(a1: &no, b1: &co) print("Swapped values are \(no), \(co)")
Когда мы запускаем вышеуказанную программу, используя площадку, мы получаем следующий результат –
Swapped values are 10, 2
Типы функций и их использование
Каждая функция следует определенной функции, рассматривая входные параметры и выводя желаемый результат.
func inputs(no1: Int, no2: Int) -> Int { return no1/no2 }
Ниже приведен пример –
Live Demo
func inputs(no1: Int, no2: Int) -> Int { return no1/no2 } print(inputs(no1: 20, no2: 10)) print(inputs(no1: 36, no2: 6))
Когда мы запускаем вышеуказанную программу, используя площадку, мы получаем следующий результат –
2 6
Здесь функция инициализируется с двумя аргументами no1 и no2 как целочисленные типы данных, а ее возвращаемый тип также объявляется как ‘int’
Func inputstr(name: String) -> String { return name }
Здесь функция объявлена как строковый тип данных.
Функции могут также иметь пустые типы данных, и такие функции не будут ничего возвращать.
Live Demo
func inputstr() { print("Swift 4 Functions") print("Types and its Usage") } inputstr()
Когда мы запускаем вышеуказанную программу, используя площадку, мы получаем следующий результат –
Swift 4 Functions Types and its Usage
Вышеуказанная функция объявлена как пустая функция без аргументов и без возвращаемых значений.
Использование типов функций
Сначала функции передаются с аргументами типа integer, float или string, а затем передаются в функцию как константы или переменные, как указано ниже.
var addition: (Int, Int) -> Int = sum
Здесь sum – это имя функции, имеющее целочисленные переменные ‘a’ и ‘b’, которое теперь объявлено как переменная для добавления имени функции. Здесь и функция сложения, и сумма имеют одинаковое количество аргументов, объявленных как целочисленный тип данных, а также возвращают целочисленные значения в качестве ссылок.
Live Demo
func sum(a: Int, b: Int) -> Int { return a + b } var addition: (Int, Int) -> Int = sum print("Result: \(addition(40, 89))")
Когда мы запускаем вышеуказанную программу, используя площадку, мы получаем следующий результат –
Result: 129
Типы функций как типы параметров и типы возврата
Мы также можем передать саму функцию в качестве типов параметров другой функции.
func sum(a: Int, b: Int) -> Int { return a + b } var addition: (Int, Int) -> Int = sum print("Result: \(addition(40, 89))") func another(addition: (Int, Int) -> Int, a: Int, b: Int) { print("Result: \(addition(a, b))") } another(sum, 10, 20)
Когда мы запускаем вышеуказанную программу, используя площадку, мы получаем следующий результат –
Result: 129 Result: 30
Вложенные функции
Вложенная функция предоставляет возможность вызывать внешнюю функцию, вызывая внутреннюю функцию.
Live Demo
func calcDecrement(forDecrement total: Int) -> () -> Int { var overallDecrement = 0 func decrementer() -> Int { overallDecrement -= total return overallDecrement } return decrementer } let decrem = calcDecrement(forDecrement: 30) print(decrem())
Когда мы запускаем вышеуказанную программу, используя площадку, мы получаем следующий результат –
Функции в Swift | Визитка программиста
Функция в Swift имеет тип, описывающий тип параметров функции и тип возвращаемого значения. Функция может быть параметром другой функции либо ее результирующим значением, они также могут вкладываться друг в друга. При объявлении функции можно задать одно или несколько именованных типизированных значений, которые будут ее входными данными (или параметрами), а также тип значения, которое функция будет передавать в качестве результата (или возвращаемый тип).
func sum(param1: Double, param2: Double) -> Double { return param1 + param2 }
Функции могут не иметь входных параметров или не иметь возвращаемого типа(в ее объявлении будет отсутствовать результирующая стрелка (->) и возвращаемый тип). Выходное значение функции может быть игнорировано:
func sum(param1: Double, param2: Double){ return param1 + param2 }
Чтобы возвращать из функции несколько значений в виде составного параметра, нужно объявить функцию типа кортеж:
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) }
Если возвращаемый из функции кортеж может иметь «пустое значение», то его следует объявить как кортеж-опционал, т. е. кортеж, который может равняться nil
. Чтобы сделать возвращаемый кортеж опционалом, нужно поставить вопросительный знак после закрывающей скобки.
Кортеж-опционал вида (Int, Int)?
это не то же самое, что кортеж, содержащий опционалы: (Int?, Int?)
. Кортеж-опционал сам является опционалом, но не обязан состоять из опциональных значений.
Чтобы при вызове функции можно было присвоить имена ее параметрам, объявляются для них внешние имена в дополнение к локальным. Внешнее имя параметра записывается перед соответствующим локальным через пробел:
//объявление функции func someFunction(externalParameterName localParameterName: Int) { // тело функции, в котором можно использовать localParameterName } //вызов такой функции someFunction(externalParameterName: 6)
Если необходимо задать внешнее имя для параметра функции, у которого уже есть подходящее локальное имя, то дублировать это имя не требуется. Вместо этого можно написать имя один раз и поставить перед ним решетку (#
):
//объявление функции func someFunction(#myParameterName: Int) { // тело функции, в котором можно использовать localParameterName } //вызов такой функции someFunction(myParameterName: 6)
При объявлении функции любому из ее параметров можно присвоить значение по умолчанию. Если у параметра есть значение по умолчанию, то при вызове функции этот параметр можно опустить:
//объявление функции func someFunction(myParameterName: String = "myValue") { // тело функции, в котором можно использовать localParameterName } //вызов такой функции someFunction(){ //myParameterName будет иметь значение "myValue" }
Для удобства язык Swift автоматически присваивает внешние имена всем параметрам, имеющим значения по умолчанию. Автоматически созданное внешнее имя совпадает с локальным, как если бы перед локальным именем стояло (#
).
Вариативным называет параметр, который может иметь сразу несколько значений или не иметь ни одного. С помощью вариативного параметра можно передать в функцию произвольное число входных значений. Чтобы объявить параметр как вариативный, нужно поставить три точки (…) после его типа:
func someFunction(numbers: Double...) -> Double { / ... for number in numbers { } // ... } someFunction(1, 2, 3, 4, 5)
У функции может быть только один вариативный параметр, который всегда должен стоять последним в списке параметров, чтобы при вызове функции, имеющей несколько параметров, не возникало двусмысленности.
По умолчанию параметры функции являются константами, поэтому попытка изменить значение параметра функции из тела этой же функции приведет к ошибке времени компиляции! Чтобы объявить параметр как переменный, нужно перед его именем поставить ключевое слово var
. Изменения переменных параметров не сохраняются после завершения работы функции и не видны за ее пределами. Переменные параметры существуют только во время работы функции.
Чтобы после завершения работы функции ее измененные параметры сохранялись, нужно объявить их не как переменные, а как сквозные параметры. Для создания сквозного параметра нужно поставить ключевое слово inout
перед объявлением параметра. Сквозной параметр передает значение в функцию, которое затем изменяется в ней и возвращается из функции, заменяя исходное значение. Аргументом для сквозного параметра может быть только переменная. Непосредственно перед именем переменной ставится амперсанд (&
), обозначающий, что аргумент, переданный как входной параметр, можно изменять в теле функции.
Сквозные параметры не могут иметь значения по умолчанию и не могут быть вариативными параметрами с ключевым словом inout. Если параметр объявлен как inout, для него уже нельзя использовать var или let. Сквозные параметры – это альтернативный способ передачи изменений, сделанных внутри функции, за пределы тела этой функции.
Помимо глобальных можно объявлять и функции, находящиеся внутри других функций, или же вложенные. Вложенные функции по умолчанию недоступны извне, а вызываются и используются только родительской функцией. Родительская функция может также возвращать одну из вложенных, чтобы вложенную функцию можно было использовать за ее пределами.
Используем замыкания в Swift по полной / Habr
Несмотря на то, что в Objective-C 2.0 присутствуют замыкания (известные как блоки), ранее эппловский API использовал их неохотно. Возможно, отчасти поэтому многие программисты под iOS с удовольствием эксплуатировали сторонние библиотеки, вроде AFNetworking, где блоки применяются повсеместно. С выходом Swift, а также добавлением новой функциональности в API, работать с замыканиями стало чрезвычайно удобно. Давайте рассмотрим, какими особенностями обладает их синтаксис в Swift, и какие трюки можно с ними «вытворять».Продвигаться будем от простого к сложному, от скучного к веселому. Заранее приношу извинения за обильное использование мантр «функция», «параметр» и «Double», но из песни слов не выкинешь.
1.1. Объекты первого класса
Для начала укрепимся с мыслью, что в Swift функции являются носителями гордого статуса объектов первого класса. Это значит, что функцию можно хранить в переменной, передавать как параметр, возвращать в качестве результата работы другой функции. Вводится понятие «типа функции». Этот тип описывает не только тип возвращаемого значения, но и типы входных аргументов.
Допустим, у нас есть две похожие функции, которые описывают две математические операции сложения и вычитания:
func add(op1: Double, op2: Double) -> Double {
return op1 + op2
}
func subtract(op1: Double, op2: Double) -> Double {
return op1 - op2
}
Их тип будет описываться следующим образом:
(Double, Double) -> Double
Прочесть это можно так: «Перед нами тип функции с двумя входными параметрами типа Double и возвращаемым значением типа Double.»
Мы можем создать переменную такого типа:
// Описываем переменную
var operation: (Double, Double) -> Double
// Смело присваиваем этой переменной значение
// нужной нам функции, в зависимости от каких-либо условий:
for i in 0..<2 {
if i == 0 {
operation = add
} else {
operation = subtract
}
let result = operation(1.0, 2.0) // "Вызываем" переменную
println(result)
}
Код, описанный выше, выведет в консоли:
3.0
-1.0
1.2. Замыкания
Используем еще одну привилегию объекта первого класса. Возвращаясь к предыдущему примеру, мы могли бы создать такую новую функцию, которая бы принимала одну из наших старых функций типа (Double, Double) -> Double в качестве последнего параметра. Вот так она будет выглядеть:
// (1)
func performOperation(op1: Double, op2: Double, operation: (Double, Double) -> Double) -> Double { // (2)
return operation(op1, op2) // (3)
}
Разберем запутанный синтаксис на составляющие. Функция performOperation принимает три параметра:
- op1 типа Double (op — сокращенное от «операнд»)
- op2 типа Double
- operation типа (Double, Double) -> Double
В своем теле performOperation просто возвращает результат выполнения функции, хранимой в параметре operation, передавая в него первых два своих параметра.
Пока что выглядит запутанно, и, возможно, даже не понятно. Немного терпения, господа.
Давайте теперь передадим в качестве третьего аргумента не переменную, а анонимную функцию, заключив ее в фигурные {} скобки. Переданный таким образом параметр и будет называться замыканием:
let result = performOperation(1.0, 2.0, {(op1: Double, op2: Double) -> Double in
return op1 + op2 // (5)
}) // (4)
println(result) // Выводит 3.0 в консоли
Отрывок кода (op1: Double, op2: Double) -> Double in — это, так сказать, «заголовок» замыкания. Состоит он из:
- псевдонимов op1, op2 типа Double для использования внутри замыкания
- возвращаемого значения замыкания -> Double
- ключевого слова in
Еще раз о том, что сейчас произошло, по пунктам:
(1) Объявлена функция performOperation
(2) Эта функция принимает три параметра. Два первых — операнды. Последний — функция, которая будет выполнена над этими операндами.
(3) performOperation возвращает результат выполнения операции.
(4) В качестве последнего параметра в performOperation была передана функция, описанная замыканием.
(5) В теле замыкания указывается, какая операция будет выполняться над операндами.
Авторы Swift приложили немало усилий, чтобы пользователи языка могли писать как можно меньше кода и как можно больше тратить свое драгоценное время на
2.1. Избавляемся от типов при вызове.
Во-первых, можно не указывать типы входных параметров в замыкании явно, так как компилятор уже знает о них. Вызов функции теперь выглядит так:
performOperation(1.0, 2.0, {(op1, op2) -> Double in
return op1 + op2
})
2.2. Используем синтаксис «хвостового замыкания».
Во-вторых, если замыкание передается в качестве последнего параметра в функцию, то синтаксис позволяет сократить запись, и код замыкания просто прикрепляется к хвосту вызова:
performOperation(1.0, 2.0) {(op1, op2) -> Double in
return op1 + op2
}
2.3. Не используем ключевое слово «return».
Приятная (в некоторых случаях) особенность языка заключается в том, что если код замыкания умещается в одну строку, то результат выполнения этой строки автоматичеси будет возвращен. Таким образом ключевое слово «return» можно не писать:
performOperation(1.0, 2.0) {(op1, op2) -> Double in
op1 + op2
}
2.4. Используем стенографические имена для параметров.
Идем дальше. Интересно, что Swift позволяет использовать так называемые стенографические (англ. shorthand) имена для входных параметров в замыкании. Т.е. каждому параметру по умолчанию присваивается псевдоним в формате $n, где n — порядковый номер параметра, начиная с нуля. Таким образом, нам, оказывается, даже не нужно придумывать имена для аргументов. В таком случае весь «заголовок» замыкания уже не несет в себе никакой смысловой нагрузки, и его можно опустить:
performOperation(1.0, 2.0) { $0 + $1 }
Согласитесь, эта запись уже совсем не похожа на ту, которая была в самом начале.
2.5. Ход конем: операторные функции.
Все это были еще цветочки. Сейчас будет ягодка.
Давайте посмотрим на предыдущую запись и зададимся вопросом, что уже знает компилятор о замыкании? Он знает количество параметров (2) и их типы (Double и Double). Знает тип возвращаемого значения (Double). Так как в коде замыкания выполняется всего одна строка, он знает, что ему нужно возвращать в качестве результата его выполнения. Можно ли упростить эту запись как-то еще?
Оказывается, можно. Если замыкание работает только с двумя входными аргументами, в качестве замыкания разрешается передать операторную функцию, которая будет выполняться над этими аргументами (операндами). Теперь наш вызов будет выглядеть следующим образом:
performOperation(1.0, 2.0, +)
Красота!
Теперь можно производить элементарные операции над нашими операндами в зависимости от некоторых условий, написав при этом минимум кода.
Кстати, Swift также позволяет использовать операции сравнения в качестве операторной фуниции. Выглядеть это будет примерно так:
func performComparisonOperation(op1: Double, op2: Double, operation: (Double, Double) -> Bool) -> Bool {
return operation(op1, op2)
}
println(performComparisonOperation(1.0, 1.0, >=)) // Выведет "true"
println(performComparisonOperation(1.0, 1.0, <)) // Выведет "false"
Или битовые операции:
func performBitwiseOperation(op1: Bool, op2: Bool, operation: (Bool, Bool) -> Bool) -> Bool {
return operation(op1, op2)
}
println(performBitwiseOperation(true, true, ^)) // Выведет "false"
println(performBitwiseOperation(true, false, |)) // Выведет "true"
Swift — в некотором роде забавный язык программирования. Надеюсь, статья будет полезной для тех, кто начинает знакомиться с этим языком, а также для тех, кому просто интересно, что там происходит у разработчиков под iOS и Mac OS X.
___________________________________________________________________
UPD.: Реальное применение
Некоторые пользователи высказали недовольство из-за отсутствия примеров из реальной жизни. Буквально вчера натолкнулся на задачу, которая может быть элегантно решена с использованием коротких замыканий.
Если вам нужно создать очередь с приоритетом, можно использовать двоичную кучу (binary heap). Как известно, это может быть как MinHeap, так и MaxHeap, т.е. кучи, где в корне дерева находится минимальный или максимальный элемент соотвественно. Базовая реализация MinHeap от MaxHeap будет отличаться по сути только проверочными сравнениями при восстановлении инварианта двоичной кучи после добавления/удаления элемента.
Таким образом, мы могли создать базовый класс BinaryHeap, который будет содержать свойство comparison типа (T, T) -> Bool. А конструктор этого класса будет принимать способ сравнения и затем использовать его в методах heapify. Прототип базового класса выглядел бы так:
class BinaryHeap<T: Comparable>: DebugPrintable {
private var array: Array<T?>
private var comparison: (T, T) -> Bool
private var used: Int = 0
// Бла-бла-бла
// Internal Methods
internal func removeTop() -> T? { //... }
internal func getTop() -> T? { //... }
// Public Methods:
func addValue(value: T) {
if used == self.array.count {
self.grow()
}
self.array[used] = value
heapifyToTop(used, comparison) // Одно из мест, где используется функция сравнения
self.used++
}
init(size newSize: Int, comparison newComparison: (T, T) -> Bool) {
array = [T?](count: newSize, repeatedValue: nil)
comparison = newComparison
}
}
Теперь для того, чтобы создать классы MinHeap и MaxHeap нам достаточно унаследоваться от BinaryHeap, а в их конструкторах просто явно указать, какое сравнение применять. Вот так будет выглядеть наши классы:
class MaxHeap<T: Comparable>: BinaryHeap<T> {
func getMax() -> T? {
return self.getTop()
}
func removeMax() -> T? {
return self.removeTop()
}
init(size newSize: Int) {
super.init(size: newSize, {$0 > $1})
}
}
class MinHeap<T: Comparable>: BinaryHeap<T> {
func getMin() -> T? {
return self.getTop()
}
func removeMin() -> T? {
return self.removeTop()
}
init(size newSize: Int) {
super.init(size: newSize, {$0 <= $1})
}
}
Анонимные функции в Swift / Habr
Эта публикация является конспектом соответствующего раздела замечательной книги «iOS 8 Programming Fundamentals with Swift» Matt Neuburg, O’Reilly, 2015. Статья, описывающая использование анонимных функций, может быть интересной новичкам или пригодится как шпаргалка для более продвинутых разработчиков.Рассмотрим пример:
func whatToAnimate() {//self.myButton является кнопкой в интерфейсе
Self.myButton.frame.origin.y += 20
}
func whatToDoLater(finished:Bool) {
printLn(“finished: \(finished)”)
}
UIView.animateWithDuration(
0.4, animations: whatToAnimate, completion: whatToDoLater)
В этом участке кода есть некоторая странность. Я объявляю функции whatToAnimate и whatToDoLater только для того, чтобы передать эти функции на следующую строку кода. И действительно, имена этих функций мне больше ни для чего не нужны – ни имена, ни эти функции больше никогда не будут повторно использованы. Было бы хорошо передавать только тела этих функций, без необходимости декларации их имен.
Такие функции называются анонимными, они имеют право на существование и часто используются в Swift.
Создание анонимной функции
Чтоб создать анонимную функцию мы делаем две вещи:
- Создаем тело функции, включая окружающие фигурные скобки, но без декларации функции.
- Если необходимо, то в первой строке внутри фигурных скобок, после команды in, описываем список аргументов функции и тип возвращаемой переменной.
Рассмотрим на примере, как преобразовать функцию в анонимную. Код исходной функции:
func whatToAnimate () {
Self.mybutton.frame.origin.y += 20
}
Вот пример анонимной функции, которая выполняет ту же задачу. Мы переместили список аргументов и возвращаемый тип внутрь фигурных скобок:
{
() -> () in
self.myButton.frame.origin.y += 20
}
Теперь, когда мы знаем, как создать анонимную функцию, посмотрим, как мы можем ее использовать. В нашем вызове animateWithDuration, точка, в которой мы вызваем функции, является местом, где мы передаем аргументы этой функции. Значит, мы можем создавать анонимную функцию прямо в этой же точке, например так:
UIView.animateWithDuration(0.4, animations: {
() -> () in
self.myButton.frame.origin.y += 20
} , completion: {
(finished:Bool) -> () in
printLn(“finished: \(finished)”)
})
Анонимные функции очень часто используются в Swift, так что следует убедиться, что мы легко читаем и пишем такой код. Анонимные функции настолько общеупотребительны и настолько важны, что существуют некоторые сокращения, используемые при их создании. Рассмотрим такие случаи.
Опускаем тип возвращаемой переменной
Если компилятор уже знает, какой тип переменной вернет функция, то мы можем опустить оператор arrow, тип и спецификацию переменной.
UIView.animateWithDuration(0.4, animations: {
() in
self.myButton.frame.origin.y += 20
}, completion: {
(finished:Bool) in
printLn(“finished: \(finished)”)
})
Опускаем строку с оператором in, когда в ней нет аргументов
Если анонимная функция не принимает аргументов и если тип возвращаемой переменной может быть опущен, то и строка с командой in может быть опущенной целиком:
UIView.animateWithDuration(0.4, animations: {
self.myButton.frame.origin.y += 20
}, сompletion: {
(finished:Bool) in
printLn(“finished: \(finished)”)
})
Опускаем типы аргументов
Если анонимная функция принимает аргументы, и их типы известны компилятору, то эти типы могут быть опущены:
UIView.animateWithDuration(0.4, animations: {
self.myButton.frame.origin.y += 20
}, completion: {
(finished) in
printLn(“finished: \(finished)”)
})
Опускаем скобки
Если типы аргументов могут быть опущены, то и скобки вокруг этих аргументов могут быть опущены:
UIView.animateWithDuration(0.4, animations: {
self.myButton.frame.origin.y += 20
}, completion: {
finished in
printLn(“finished: \(finished)”)
})
Опускаем строку с in, даже если в ней есть аргументы
Если возвращаемый тип может быть опущен, и если типы аргументов известны компилятору, вы можете опустить строку с in и ссылаться на аргументы прямо из тела анонимной функции используя имена $0, $1 и так далее по порядку:
UIView.animateWithDuration(0.4, animations: {
self.myButton.frame.origin.y += 20
}, completion: {
printLn(“finished: \($0)”)
})
Опускаем имена аргументов
Если ваша анонимная функция не нуждается в аргументах, то вы можете опустить аргументы, заменяя их имена знаком подчеркивания в строке с in:
UIView.animateWithDuration(0.4, animations: {
self.myButton.frame.origin.y += 20
}, completion: {
_ in
printLn(“finished!”)
})
Примечание. Если ваша анонимная функция принимает аргументы, вы должны их так или иначе использовать. Вы можете опустить строку с in и использовать аргументы по их именам типа $0, $1 и так далее, или вы можете сохранить строку с in и заменить аргументы на знак подчеркивания, но вы не можете одновременно и опускать строку с in, и не использовать аргументы по их именам $0, $1. Если вы так поступите, ваш код не будет скомпилирован.
Опускаем имена аргументов вызывающей функции
Если ваша анонимная функция является последним параметром, который передается в вызове, то вы можете закрыть вызов скобкой до последнего параметра, а затем поставить тело анонимной функции без указания имен аргументов. Такой способ называется замыкающей функцией (trailing function).
UIView.animateWithDuration(0.4, animations: {
self.myButton.frame.origin.y += 20
}) {
_ in
printLn(“finished!”)
})
Опускаем скобки вызывающей функции
Если вы используете замыкающую функцию и вызываемая вами функцияне принимает других параметров, кроме передаваемой вами функции, вы можете опустить пустые скобки в вызове. Для примера я объявлю и вызову другую функцию:
func doThis(f: () -> ()) {
f()
}
doThis { // тут опускаем скобки
printLn(“Hello”)
}
Это единственная ситуация, в которой вы можете опустить скобки в вызове функции.
Опускаем ключевое слово return
Если тело анонимной функции состоит только из одного утверждения и это утверждение состоит из возвращаемой величины с ключевым словом return, то ключевое слово return может быть опущено. Иначе говоря, в ситуации когда ожидаем, что функция вернет величину, если тело анонимной функции состоит только из одного утверждения, Swift подразумевает, что это утверждение является выражением, чье значение будет возвращено из анонимной функции:
func sayHello() -> String {
return “Hello”
}
func performAndPrint(f: () -> String) {
let s = f()
printLn(s)
}
performAndPrint {
sayHello() //подразумевая return sayHello() в функции performAndPrint
}
Практическое использование
Рассмотрим пример. У нас есть массив переменных и нам нужно получить новый массив, состоящий из тех же величин, умноженных на 2, вызванных при помощи метода map. Метод массива map принимает функцию, которая принимает аргумент и возвращает переменную того же типа, как и элемент массива. Наш массив состоит из переменных Int, следовательно мы будем передавать методу map функцию, которая принимает параметр Int и возвращает Int. Мы могли бы написать целую функцию, например такую
let arr = [2, 3, 4, 5, 6]
func doubleMe(i:Int) -> {
return i*2
}
let arr2 = arr.map(doubleMe)
Однако такой подход не полностью использует возможности Swift. Нам больше нигде не нужна функция doubleMe, так что она вполне может стать анонимной. Тип возвращаемой ею переменной известен, так что нам не нужно указывать его. Тут всего лишь один параметр, и мы собираемся использовать его, но нам не нужна строка с in, если мы ссылаемся на параметр как $0. Тело нашей функции состоит из одного утверждения, и это return, так что мы можем опустить return. И функция map не принимает каких-либо других параметров, так что мы можем опустить скобки и сразу после анонимной функции указывать имя:
let arr = [2, 3, 4, 5, 6]
let arr2 = arr.map {$0*2}
Если начать пользоваться анонимными функциями, то вы обнаружите, что часто пользуетесь преимуществами, которыми они предоставляют. Вдобавок, вы часто будете сокращать объем кода (но не в ущерб коду), помещая целые анонимные функции в одну строку с вызовом функции.