Functions are self-contained chunks of code that perform a specific task. You give a function a name that identifies what it does, and this name is used to “call” the function to perform its task when needed.
함수 (Functions) 는 "특정 임무를 수행하는 독립된 코드 조각" 입니다. 함수의 역할을 식별하는 이름을 지정하고, 필요할 때 이 이름을 사용하여 임무를 수행할 함수를 호출 (call) 합니다.
스위프트의 통합 함수 구문은 매개 변수가 없는 단순한 C-스타일 메서드 부터 각 매개변수에 대한 이름과 아규먼트 레이블이 있는 복잡한 Objective-C-스타일 메서드까지 어떤 것이든 표현할 만큼 충분히 유연하다. 매개 변수는 함수 호출이 단순해지도록 기본값을 제공할 수도 있고, 함수 실행을 한 번 완료하고 나면 전달한 변수 (매개 변수) 를 수정하는 입-출력 (in-out) 매개 변수로 전달할 수도 있다.
스위프트의 모든 함수는, 함수의 매개 변수 타입과 반환타입으로 구성된 타입을 가진다. 스위프트의 다른 타입과 마찬가지로 이 타입을 사용할 수 있기 때문에, 함수를 매개 변수로 다른 함수에 전달하고 함수에서 함수를 반환할 수 있다. 또한 함수는 내포된 함수 범위 내에서 유용한 기능을 캡슐화하기 위해 다른 함수 내에서 작성할 수 있다.
함수를 정의할 때 매개변수 (parameters) 라는 이름과 타입을 지정한 값을 선택적으로 정의할 수 있다. 또한 함수가 완료되었을 때 출력으로 반환되는 값의 타입을 반환타입 (return type) 을 선택적으로 정의할 수도 있다.
모든 함수에는 함수가 수행할 작업을 설명하는 함수 이름 (function name) 이 있다. 함수를 사용하려면 함수를 이름으로 호출하고 함수의 매개 변수 타입과 일치하는 (인자 (arguments) 라고 하는) 입력 값을 전달한다. 함수의 인자는 반드시 함수 매개 변수 목록과 같은 순서로 제공해야 한다.
아래 예제에 있는 함수는 하는 작업이 "사람 이름을 입력받아 해당 사람에게 인사말을 반환"하는 것이기 때문에 greet(person:)
이라는 이름으로 호출된다. 이 작업을 수행하기 위해 person
이라는 String
값을 입력받는 매개변수 하나와, 해당 사람에게 할 인사말을 담을 String
반환 타입을 정의한다.
func greet(person: String) -> String {
let greeting = "Hello, " + person + "!"
return greeting
}
func
키워드 뒤에 있는 함수의 정의 (definition) 로 끌어모아 지정한다. 함수의 반환 타입은 (대쉬 기호 다음에 오른쪽 꺽쇠 괄호 기호를 붙인) 반환 화살표 (return arrow) ->
와 그 다음에 반환할 타입의 이름으로 지시합니다.print(greet(person: "Wonbi"))
// "Hello, Wonbi!" 를 인쇄함
print(greet(person: "Sunny"))
// "Hello, Sunny!" 를 인쇄함
greet(person:)
함수는, greet(person: "Anna")
같이, person
인자 이름표 뒤에 String
값을 전달하여 호출한다. 함수가 String
값을 반환하기 때문에, 위에 보는 것처럼, print(_:separator:terminator:)
함수 안에 greet(person:)
함수를 포장한 채로 호출하여 해당 문자열을 인쇄하고 반환 값을 볼 수 있다.
greet(person:)
함수 본문은 greeting
이라는 새로운 String
상수를 정의하고 단순한 인사말 메시지를 설정하는 것으로 시작한다. 그다음 return
키워드로 이 인사말을 함수 밖으로 반환한다.
서로 다른 입력 값을 가지고 greet(person:)
함수를 여러 번 호출할 수도 있다. 위 예제는 "Wonbi"
라는 입력 값과, "Sunny"
이라는 입력 값을 가지고 호출하면 무슨 일이 발생하는 지를 보여준다. 함수는 각 경우마다 맞춤 인사말을 반환한다.
메시지 생성문과 반환문을 한 줄로 조합하면 함수 본문을 더 짧게 만들수도 있다.
func greetAgain(person: String) -> String {
return "Hello again, " + person + "!"
}
print(greetAgain(person: "Sunny"))
// "Hello again, Sunny!" 를 인쇄함
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: "Wonbi", alreadyGreeted: true))
// "Hello again, Wonbi!" 를 인쇄함
greet(person:alreadyGreeted:)
함수는 괄호 안에 person
매개변수에 String
값과 alreadyGreeted
매개변수의 Bool
값을 쉼표로 구분하여 전달함으로써 호출한다. 이 함수는 앞 부분에서 본 greet(person:)
함수와 서로 별개라는 것을 기억하자. 두 함수 다 greet
으로 시작하는 이름을 가지긴 하지만, greet(person:alreadyGreeted:)
함수는 두 매개변수를 받지만 greet(person:)
함수는 하나만 받는다.String
값을 반환하기 보다 인쇄를 하는 버전의 greet(person:)
함수이다.func greet(person: String) {
print("Hello, \(person)!")
}
greet(person: "Moon")
// "Hello, Moon!" 를 인쇄함
값을 반환할 필요가 없기 때문에, 함수를 정의할 때 반환 화살표 (->
) 나 반환 타입을 포함하지 않는다.
엄밀히 말해서, 이 버전의
greet(person:)
함수가, 반환 값을 정의하지 않았을지라도, 여전히 값을 반환 합니다 (does). 반환 타입을 정의하지 않은 함수는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:)
은 첫 번째 함수를 호출하지만 반환 값은 무시한다. 두 번째 함수가 호출되면 첫 번째 함수에 의해 메시지는 여전히 인쇄되지만 반환 값은 사용하지 않는다.반환 값을 무시할 순 있지만, 값을 반환할거라고 한 함수는 반드시 항상 그래야 합니다. 반환 타입을 정의한 함수는 값을 반환하지 않고 함수 밑을 빠져나가는 제어를 허용하지 않으며, 그럴려는 시도는 컴파일 에러가 될 것입니다.
inMax(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
라는 이름표가 붙어 있어서 함수 반환 값을 조회할 때 이름으로 접근할 수 있다.
튜플의 멤버 값은 함수 반환 타입에서 이름을 붙였기 때문에, 점 구문 (dot syntax) 으로 접근해서 최소 및 최대 값을 가져올 수 있다.
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
인자가 빈 배열을 담고 있다면, 위에 정의한 minMax(array:)
함수가 array[0]
에 접근하려고 할 때 런타임 에러를 발동할 것이다.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 greeting(for person: String) -> String {
"Hello, " + person + "!"
}
print(greeting(for: "Dave"))
// "Hello, Dave!" 를 인쇄함
func someFunction (firstParameterName: Int, secondParameterName: Int) {
// 함수 본문에서, firstParameterName 과 secondParameterName 은
// 첫 번째 및 두 번째 매개 변수의 인자 값을 참조합니다.
}
someFunction(firstParameterName: 1, secondParameterName: 2)
func someFunction(argumentLabel parameterName: Int) {
// 함수 본문에서, parameterName 은
// 해당 매개 변수의 인자 값을 참조합니다.
}
func greet(person: String, from hometown: String) -> String {
return "안녕하세요 \(person)! \(hometown)으로부터의 방문을 환영합니다."
}
print (greet(person: "Bill", from: "Cupertino"))
// "안녕하세요 Bill! 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 임
가변 매개변수 (variadic parameter) 는 특정 타입의 값을 0개, 혹은 그 이상 받는다. 가변 매개변수는 함수 호출할 때 매개변수에다가 다양한 개수의 입력 값을 전달할 수 있음을 지정하고자 할 때 사용한다. 매개변수의 타입 이름 뒤에 마침표 문자 세 개 (...) 를 집어 넣어서 가변 매개변수를 작성한다.
가변 매개변수에 전달한 값은 함수 본문 안에서 적절한 타입의 배열로 사용이 가능하다. 예를 들어, numbers
라는 이름과 Double...
이라는 타입을 가진 가변 매개변수는 함수 본문 안에서 [Double]
타입의 numbers
라는 상수 배열로 사용이 가능하다.
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) 되는 값을 가지게 된다. 입-출력 매개변수 및 이와 관련된 컴파일러 최적화의 동작에 대한 더 자세한 논의는, In-Out Parameters (입-출력 매개 변수) 부분을 참고하면 된다.
변수만 입-출력 매개변수 인자로 전달할 수 있다. 상수나 글자 값 (literal) 은 인자로 전달할 수 없는데, 상수와 글자 값은 수정할 수 없기 때문이다. 입-출력 매개변수의 인자로 전달할 때는, 함수가 수정할 수 있음을 지시하기 위해, 변수 이름 바로 앞에 ‘앰퍼샌드 (ampersand; &
)’ 를 붙인다.
입-출력 매개 변수는 기본 (default) 값을 가질 수 없으며, 가변 매개 변수는
inout
으로 표시할 수 없습니다.
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
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 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!