본문 바로가기

Swift/Swift Language

Swift 언어 가이드 - 함수

함수

_함수(function)_는 특정한 기능를 수행하기 위한 청크 단위의 코드이며 독립적인 코드입니다. 함수에 식별할 수 있는 이름을 부여할 수 있습니다. 함수의 이름은 함수가 기능을 수행할 때 함수를 "호출(call)"할 때 사용됩니다.

스위프트의 통합된 함수 문법은 유연합니다. 인자의 이름이 없는 간단한 C 형식의 함수를 표현하는 것 부터, 함수의 파라미터에 이름이 매겨진 복잡한 Objective-C 형식의 메서드까지도 표현할 수 있습니다. 파라미터는 기본 값을 가질 수 있습니다. 또한, 함수가 실행을 종료할 때 전달된 변수를 수정하는 _in-out 파라미터_로 인자를 전달할 수 있습니다.

스위프트의 모든 함수는 타입을 가집니다. 함수의 파라미터의 타입과 리턴 타입이 그것입니다. 여기서 사용되는 타입들은 스위프트의 기본 타입을 포함하며, 함수를 다른 함수의 파라미터로 전달할 수 있습니다. 또한, 함수는 다른 함수 안에 작성되어 유용한 기능들을 함수 안에 중첩하여 캡슐화할 수 있습니다.

함수를 정의하고 호출하기

여러분이 함수를 정의할 때, 이름과 타입이 있는 하나 이상의 값들을 함수의 입력 값으로 사용할 수 있습니다. 이것을 _파라미터(parameters)_라고 합니다. 여러분은 또한, 어떠한 타입을 정의하여 함수가 출력으로 돌려 줄 값을 나타낼 수 있습니다. 이것을 _리턴 타입(return type)_이라고 합니다.

모든 함수는 이름을 가집니다. 이 이름은 함수가 수행할 기능을 설명합니다. 함수를 사용하려면, 여러분은 함수를 그것의 이름과 입력할 값(인자)를 가지고 "호출"해야 합니다. 함수의 인자는 반드시 함수의 파라미터 목록과 같은 순서로 작성해야 합니다.

아래 예제의 함수는 greet(person:)이라고 하는 함수입니다. 이것은 사람의 이름을 입력으로 받아서 안부 인사를 반환합니다. 이 함수를 완성하려면 여러분은 하나의 입력 파라미터인 String 값을 명시해야 합니다. 이것을 person으로 명명하겠습니다. 그리고, 리턴 타입으로 String 으로 명시하여 인자 값으로 받은 사람에게 안부 인사를 만들어 반환합니다.

func greet(person: String) -> String {
  let greeting = "Hello, " + person + "!"
  return greeting
}

함수의 선언 부분에 필요한 모든 정보들이 들어있습니다. 이 함수는 func 이라는 키워드로 시작합니다. 그리고 함수의 리턴 타입은 return arrow -> 이 대신합니다. 이 화살표 이후에 반환할 타입의 이름이 옵니다.

이 함수의 정의는 함수가 무엇을 하는지를 말해줍니다. 그리고 무엇을 돌려 받길 원하는지도 알 수 있으며, 무엇이 반환 값인지도 확인할 수 있습니다. 이러한 방식의 정의는 함수를 모호하지 않게 호출 할 수 있도록 해줍니다.

print(greet(person: "Anna"))
// "Hello, Anna!"가 출력됨
print(greet(person: "Biran"))
// "Hello, Brain!"이 출력됨

여러분은 함수를 호출할 떄, person이라는 인자 레이블 뒤에 String 값을 가지고 greet(person:) 함수를 greet(person: "Anna")로 호출합니다. 이 함수는 String 값을 반환하므로 greet(person:)print(_:separator:terminator:)의 호출에 사용됩니다.

참고

print(_:separator:terminator:) 함수는 첫 번째 인자는 레이블을 가지지 않습니다. 그리고 다른 인자는 기본 값을 가지므로 부가적입니다. 이러한 함수 문법을 다양화하는 것에 대해서는 Function Argument Labels and Parameter NamesDefault Parameter Values를 참고하세요.

함수 greet(person:) 의 내부에서 이 함수는 String 타입의 greeting이라는 새로운 변수를 정의하고 간단한 메시지를 만들면서 시작합니다. 이 메시지는 return 키워드를 사용하여 함수가 반환할 대상이 됩니다. return greeting이 있는 코드의 줄에서 함수는 실행을 종료하고 greeting의 값을 반환합니다.

여러분은 greet(person:) 함수를 다른 입력 값을 가지고 여러번 호출할 수 있습니다. 위의 예제에서 "Anna"라는 입력 값을 가지고 호출하면 어떤 일이 일어나는지를 확인할 수 있습니다. 그리고 "Brain"을 입력값으로 둡니다. 이 함수는 각각의 경우에 맞춘 안부 인사를 반환합니다.

이 함수의 내부를 좀 더 간략하게 만들려면, 리턴 문장에 메시지를 조합하여 만들 수 있습니다.

func greetAgain(person: String) -> String {
  return "Hello again, " + person + "!"
}
print(greetAgain(person: "Anna"))
// "Hello again, Anna!"가 출력됨

함수 파라미터와 리턴 값

함수 파라미터와 리턴 값은 스위프트에서 매우 유연하게 사용됩니다. 여러분은 간단한 함수와 하나의 이름없는 파라미터를 정의하거나, 표현식을 사용한 파라미터 혹은 파라미터 옵션들을 함께 사용할 수도 있습니다.

파라미터 없는 함수

함수는 입력 파라미터를 필요로 하지 않을 수도 있습니다. 이 예제의 함수는 입력 파라미터가 없으며 항상 동일한 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:) 함수를 호출할 때, person이라고 이름 지어진 String 인자 값과 alreadyGreeted라는 이름의 Bool 값을 인자로 받습니다. 이 함수는 greet(person:) 함수와 구별된다는 점에 유의하세요. 두 함수 모두 greet으로 시작하는 이름이지만 greet(person:alreadyGreeted:) 함수는 두 가지 인자를 입력으로 합니다.

리턴 값이 없는 함수

리턴 타입을 명시할 필요가 없는 함수도 있습니다. 이전의 greet(person:)함수를 수정하여, String 값을 반환하는 대신 출력하도록 하는 예제를 보겠습니다.

func greet(person: String) {
  print("Hello, \(person)!")
}
greet(person: "Dave")
// "Hello, Dave!"가 출력됨

이 함수는 리턴 값이 필요 없으므로, 화살표 (->)와 리턴 타입을 명시하지 않습니다.

참고

엄격하게 말하자면, greet(person:) 함수는 값을 리턴한다는 명시를 하지 않았지만 여전히 값을 리턴합니다. 리턴 타입이 작성되지 않은 함수는 특별한 값인 Void라는 타입의 값을 반환합니다. 이것은 단순히 말하자면 ()로 작성되는 비어 있는 튜플(tuple)입니다.

어떠한 함수의 리턴 값은 사용되지 않을 수도 있습니다.

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 값을 포함한 튜플을 반환합니다. 이 값들은 minmax로 명시되어 함수의 리턴 값을 가져 올 때 이름으로 가져올 수 있도록 해줍니다.

이 함수의 내부를 살펴보면, currentMin, currentMax라는 배열의 첫 번째 정수 값을 변수를 설정하여 시작합니다. 그리고 나서, 이 함수는 배열에서 남아 있는 값들을 순회하여 각각의 값들이 가장 작은 값인지, 혹은 가장 큰 값인지를 확인합니다. 마지막으로, 최소 값과 최대 값이 튜플의 두 Int 값으로 반환됩니다.

튜플의 멤버 값들이 함수의 리턴 값으로 명시되었으므로, 점(.) 을 이용한 문법으로 반환 값에 접근할 수 있습니다.

let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("최소 값은 \(bounds.min) 이며, 최대 값은 \(bounds.max)")
// "최소 값은 -6 이며, 최대 값은 109"가 출력됨

유의할 점은 튜플의 멤버들은 튜플이 함수에서 반환되는 시점에는 이름을 붙일 필요가 없다는 점입니다. 왜냐하면 함수의 반환 값으로 이미 명시 되어 있기 때문입니다.

옵셔널 튜플 리턴 타입

만약 함수로 반환될 튜플 타입이 "어떠한 값도 없는" 상태일 수도 있다면, 여러분은 옵셔널 튜플 리턴 타입을 사용할 수 있습니다. 이 타입은 튜플이 nil일 수도 있음을 알려줍니다. 튜플의 닫는 소괄호 뒤에 물음표를 붙여서 옵셔널 튜플 리턴 타입을 사용할 수 있습니다. 예를 들어 (Int, Int)?, 또는 (String, Int, Bool)?이 있습니다.

참고

옵셔널 튜플 타입인 (Int, Int)?은 옵셔널 타입을 포함한 튜플인 (Int?, Int?)와는 전혀 다릅니다. 옵셔널 튜플 타입의 경우, 튜플 안의 각각의 값이 옵셔널이 아닌 튜플 자체가 옵셔널입니다.

minMax(array:) 함수는 두 개의 Int 값을 포함한 튜플을 반환합니다. 하지만, 배열을 전달받을 떄 어떠한 안정성 검사(safety checks)를 하지 않습니다. 만약 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)
}

여러분은 옵셔널 바인딩(optional binding)을 사용하여 minMax(array:) 함수가 실제 튜플을 반환하는지 nil을 반환하는지 검사할 수 있습니다.

if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
  print("최소 값은 \(bounds.min) 이고, 최대 값은 \(bounds.max)")
}
// "최소 값은 -6 이고, 최대 값은 109"이 출력됨

함축적 리턴(Implicit Return)

함수 내부의 전체가 하나의 표현식을 사용한다면, 함수는 함축적으로 그 표현식을 반환합니다. 예를 들어, 아래의 두 함수는 같은 기능을 합니다.

func greeting(for person: String) -> String {
  "Hello, " + person + "!"
}
print(greeting(for: "Dave"))
// "Hello, Dave!"가 출력됨

func anotherGreeting(for person: String) -> String {
  return "Hello, " + person + "!"
}
print(anotherGreeting(for: "Dave"))
// "Hello, Dave!"가 출력됨

greeting(for:)함수는 인삿말을 표현한 것이 전부입니다. anotherGreeting(for:) 함수는 동일한 인삿말을 반환하지만, return 키워드를 사용하여 조금 더 코드가 길어졌습니다. 어떠한 함수든 하나의 줄을 작성하여 return할 수도 있고 return을 생략할 수도 있습니다.

함수의 인자 레이블과 파라미터 이름

함수의 인자는 인자 레이블(argument label)파라미터 이름(parameter name) 두 가지를 모두 가질 수 있습니다. 인자 레이블은 함수를 호출 할 때 사용됩니다. 파라미터 이름은 함수의 구현 부분에서 사용됩니다. 기본적으로 파라미터 이름을 인자 레이블로 사용합니다.

func someFunction(firstParameterName: Int, secondParameterName: Int) {
  // 함수의 내부에서, firstParameterName과 secondParameterName
  // 이 두 파라미터는 인자 값들과 같다.
}
someFunction(firstParameterName: 1, secondParameterName: 2)

모든 파라미터는 유일한 이름을 가져야 합니다. 여러 파라미터 들이 인자 레이블과 같을 수 있지만 유일한 인자 레이블은 코드를 좀 더 가독성있게 합니다.

인자 레이블 명시하기

인자 레이블은 파라미터 이름 앞에 공백으로 구분하여 작성합니다.

func someFunction(argumentLabel parameterName: Int) {
  //...
}

여기에 greet(person:) 함수를 변형한 예제가 있습니다. 이 함수는 사람의 이름과 지역을 받아서 인삿말을 반환합니다.

func greet(person: String, from hometown: String) -> String {
  return "안녕 \(person)! \(hometown)에 온 것을 환영합니다."
}
print(greet(person: "Bill", from: "Cupertino"))
// "안녕 Bill! Cupertino에 온 것을 환영합니다."

인자 레이블을 사용하는 것은 함수를 명시적이며, 문장을 읽는 방식처럼 호출하도록 해줍니다. 또한, 함수의 내부를 명확한 의도와 읽기 쉽도록 해줍니다.

인자 레이블 생략하기

만약 여러분이 어떤 파라미터에 대한 인자 레이블을 원하지 않는다면, 언더스코어(_)를 사용할 수 있습니다.

func someFunction(_ firstParamterName: Int, secondParametrName: Int) {
    // ...
}
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

기본 값을 작성하지 않은 파라미터를 함수 파라미터의 앞에 작성하세요. 그리고 기본 값이 있는 파라미터를 뒤에 작성하세요. 기본 값이 없는 파라미터는 주로 함수에서 중요한 역할을 합니다. 따라서, 함수가 호출 될 때 좀 더 쉽게 인식할 수 있습니다.

가변 파라미터(Variadic Parameters)

가변 파라미터(variadic parameter)는 0개 혹은 그 이상의 개수의 구체적인 타입의 값들을 가진 것을 말합니다. 여러분은 가변 파라미터를 함수를 호출 할 때 입력 값의 개수가 정해지지 않을 때 사용할 수 있습니다. 가변 파라미터는 세 개의 점 문자(...)를 파라미터의 타입 이름 뒤에 붙여서 사용할 수 있습니다.

가변 인자에 전달된 값들은 적절한 타입의 배열로서 함수 내부에서 사용됩니다. 예를 들어, 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을 반환한다.

참고

함수는 최대 하나의 가변 파라미터만을 가집니다.

입-출력 파라미터

함수 파라미터는 기본적으로 상수입니다.함수 내부에서 함수 파라미터의 값을 바꾸려고 하면 컴파일 타임 에러를 발생합니다. 다시 말해, 여러분은 파라미터를 실수로 바꿀 염려를 할 필요가 없다는 것입니다. 만약 파라미터의 함수를 밖고 싶을 때, 구체적으로는 함수가 종료되고 해당 파라미터의 값을 변경하고 싶다면 입-출력 파라미터(in-out parameter)를 사용하면 됩니다.

입-출력 파라미터는 inout 키워드를 파라미터 타입 바로 앞에 사용합니다. 입-출력 파라미터는 함수에 전달되어 함수에 의해 수정되며 함수에서 빠져나올 때 원본 값을 교체하게 됩니다. 좀 더 자세한 입-출력 파라미터 내부의 동작과 이것과 연관된 컴파일러 최적화에 대해서는 입-출력 파라미터 (In-Out Parameters)를 참고하세요.

여러분은 오로지 입-출력 파라미터를 대신하는 인자인 변수를 전달합니다. 여러분은 상수나 리터럴 값을 전달할 수 없습니다. 왜냐하면 상수나 리터럴은 수정할 수 없기 때문입니다. 앰퍼샌드(&)기호를 변수 이름 앞에 두어 이것을 인자로 전달하게 됩니다. 이것은 함수에게 이 인자가 수정될 수 있다는 것을 말해줍니다.

참고

입-출력 파라미터는 기본 값을 가질 수 없으며 가변 파라미터는 inout으로 표현될 수 없습니다.

여기에 swapTwoInts(_:_:)라는 함수가 있습니다. 이 함수는 입-출력 정수 파라미터인 ab를 받습니다.

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
  let temporaryA = a
  a = b
  b = temporaryA
}

swapTwoInts(_:_:) 함수는 단순히 ba의 값을 서로 교체하는 역할을 합니다. 이 함수는 임시 상수인 temporaryA의 값을 저장하므로써 교환이 이루어집니다. 그리고 b의 값을 a에 할당하고 temporaryAb에 할당합니다.

여러분은 swapTwoInts(_:_:) 함수를 두 개의 Int 타입 변수로 호출하여 그 두 변수의 값을 서로 교환할 수 있습니다. someIntanotherInt는 앰퍼샌드(&)가 앞에 붙여져 있다는 것을 확인해보세요.

var someInt = 3
var anotherInt = 107
swapTwoints(&someInt, &anotherInt)
print("someInt는 이제 \(someInt)이며, anotherInt는 이제 \(anotherInt)이다.")
// "someInt는 이제 107이며, anotherInt는 이제 3이다."가 출력됨

someInt의 원본 값과 anotherInt의 원본 값이 swapTwoInts(_:_:) 함수 바깥에 있지만 수정된 것을 알 수 있습니다.

입-출력 파라미터는 함수에 의해 값을 반환하는 것과는 다릅니다. swapTwoInts 예제는 리턴 타입을 명시하기 않으며 값을 반환하지 않습니다. 하지만 여전히 someIntanotherInt의 값을 수정합니다. 입-출력 파라미터는 함수 내부 스코프 바깥에서 함수가 변수에 영향을 미치기 위한 하나의 방법입니다.

함수 타입

모든 함수는 특정한 함수 타입(function type)을 가집니다. 이것은 파라미터 타입과 함수의 리턴 타입으로 이루어져 있습니다.

예를 들어:

func addTwoInts(_ a: Int, _ b: Int) -> Int {
  return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
  return a * b
}

이 예제는 addTwoIntsmultiplyTwoInts라는 두 개의 수학적 함수를 정의합니다. 이 함수들은 두 개의 Int 값을 인자로 받고, Int 값을 리턴합니다.

두 함수의 타입은 모두 (Int, Int) -> Int입니다. 이것을 말로 풀어서 설명하면 :

"이 함수는 두 파라미터를 가진다. 두 파라미터의 타입은 Int이며 반환 값은 Int 타입이다."

아래의 예제는 파라미터와 리턴 값이 없는 함수입니다.

func printHelloWorld() {
  print("hello, world")
}

이 함수는 () -> Void라는 타입을 가졌다고 말할 수 있으며, "파라미터가 없으며 반환 값이 Void인 함수"라고도 설명할 수 있습니다.

함수 타입 사용하기

스위프트에서 여러분은 다른 어떤 타입들 처럼 함수 타입을 사용할 수 있습니다. 예를 들어, 여러분은 상수나 변수를 함수의 타입으로 정의하여 그 상수나 변수에 적절한 함수를 할당 할 수 있습니다.

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

이것을 설명하면 다음과 같습니다 :

"변수를 mathfunction으로 정의하고, 이 변수의 타입을 '두 Int를 전달 받으며 Int 값을 반환하는 함수'로 정의 하라. addTwoInts라는 함수에 이 변수를 할당하라"

addTwoInts(_:_:)라는 함수는 mathFunction 변수와 같은 타입입니다. 따라서 스위프트의 타입-검사기에 의해 할당이 가능합니다.

이제 여러분은 할당된 함수인 mathFunction을 호출할 수 있습니다.

print("결과: \(mathFunction(2, 3))")
// "결과: 5"가 출력됨

함수가 아닌 타입을 할당하듯이, 타입이 서로 일치하는 다른 함수 또한 변수로 할당 할 수 있습니다.

mathFunction = multiplyTwoInts
print("결과 : \(mathFunction(2, 3))")
// "결과: 6"

다른 타입 처럼, 함수를 상수나 변수에 할당할 때, 여러분은 Swift가 함수의 타입을 추론하도록 내버려 둘 수 있습니다.

let anotherMathFunction = addTwoInts
// anotherMathFunction은 (Int, Int) -> Int 타입으로 추론됨

파라미터 타입이 되는 함수 타입

여러분은 (Int, Int) -> Int와 같은함수 타입을 다른 함수의 파라미터 타입으로 사용할 수 있습니다. 이러한 기능은 함수 호출을 다른 함수의 구현 부분에 사용할 수 있습니다.

수학 함수의 결과를 출력하는 예제를 살펴보겠습니다.

func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
  print("결과: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// "결과: 8"가 출력됨

이 예제에서는 세 가지 파라미터를 인자로 받는 printMathResult(_:_:_:) 함수를 보여주니다. 첫 번째 파라미터인 mathFunction은 타입이 (Int, Int) -> Int로서, 어떠한 함수든 이 타입과 일치한다면 이 함수의 첫 번째 파라미터로 사용할 수 있습니다. 두 번째와 세 번째 함수는 ab입니다. 이 두 파라미터 모두 Int타입입니다. 이 파라미터는 인자로 받은 함수를 호출 할 때 사용됩니다.

printMathResult(_:_:_:)가 호출되면, addTwoInts(_:_:) 함수와 35 정수가 전달됩니다. 그리고 나서, 전달 받은 함수는 35를 인자로 하여 호출됩니다. 그리고 8이라는 결과를 출력합니다.

printMathResult(_:_:_:)의 역할은 적절한 타입의 수학 함수를 호출하여 결과를 출력하기 위함입니다. 어떠한 함수를 인자로 받든지 상관 없이 올바른 타입이라면 가능합니다.

리턴 타입이되는 함수 타입

여러분은 함수 타입을 다른 함수의 리턴 타입으로 사용할 수 있습니다. 함수를 반환하는 기호인 화살표 (->) 뒤에 완전한 함수의 타입을 작성합니다.

다음 예제는 두개의 간단한 함수인 stepForward(_:)stepBackward(_:)입니다. stepForward(_:)는 입력 값에 하나를 더한 값을 반환하고 stepBackward(_:)는 입력에 하나를 뺀 값을 반환합니다. 두 함수 모두 (Int) -> Int 타입입니다.

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

아래의 함수 chooseStepFuntion(backward:)는 반환 타입이 (Int) -> Int입니다. chooseStepFunction(backward:) 함수는 backward라는 불리언 파라미터에 따라서 stepFoward(_:), 혹은 stepBackward(_:) 함수를 반환합니다.

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
  return backward ? stepBackward : stepForward
}

여러분은 두 가지 방향 중 하나를 선택하여 함수를 얻기위해 chooseStepFunction(backward:)를 사용할 수 있습니다.

var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero는 이제 stepBackward() 함수를 참조하게 됩니다.

위 예제는 currentValue라는 변수가 점진적으로 0에 다가가기 위해 음수, 양수의 값을 선택하게 됩니다. currentValue는 초기에 3을 가지므로 currentValue > 0true을 반환합니다. 결국엔, chooseStepFunction(backward:) 함수는 stepBackward(_:) 함수를 반환합니다. 반환된 함수에 대한 참조는 moveNearerToZero라는 상수에 저장됩니다.

이제 moveNearerToZero가 올바른 함수를 가리키게 됩니다. 이것을 0까지 세기 위해 사용할 수도 있습니다.

print("Counting to zero:")
// Counting to zero:
while currentValue != - {
  print("\(currentValue)...")
  currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// 3...
// 2...
// 1...
// zero!

중첩 함수

여러분이 지금까지 살펴본 모든 함수는 전역 함수(global function)입니다. 이 함수는 전역으로 범위를 갖습니다. 여러분은 또한 어떤 함수의 내부에 다른 함수를 정의할 수 있습니다. 이것을 중첩 함수(nested function)이라고 합니다.

중첩 함수는 바깥에서는 보이지 않습니다. 하지만 그 함수를 감싸는 함수에 의해 호출되거나 사용될 수 있습니다. 감싸는 함수는 중첩 함수 중 하나를 반환하여 다른 또 다른 스코프에서 그 중첩 함수를 사용할 수도 있습니다.

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 != - {
  print("\(currentValue)...")
  currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!

출처 : https://docs.swift.org/swift-book/LanguageGuide/Functions.html