함수는 특정한 업무를 수행하기 위해 모아놓은 코드 뭉치라고 할 수 있습니다.
함수를 사용하는 것은 "call"(호출) 이라고 표현하며 필요할 때 호출할 수 있습니다.
Swift 의 함수는 매개변수(parameters) 에 이름을 사용하지 않을 수 있으며, 기본 값을 지정해줄 수도 있는 등 다양한 편의를 제공합니다.
한편 모든 Swift 의 함수들은 자료형을 가지며, 매개변수 자료형과 반환값(Return) 자료형을 포함하고 있습니다. 함수의 자료형들은 다른 자료형과 마찬가지로 자유롭게 사용할 수 있으며, 함수 내에서 함수를 호출 하는 등의 기능도 제공하고 있습니다.
함수를 정의할 때, 함수에는 이름이 필요합니다.
또한 함수가 입력값 - 매개변수(파라미터) - 을 받거나, 함수가 종료될 때 반환할 값 - 리턴 값(return)- 을 받는다면 추가적으로 함수를 정의할 때 함께 정의해야 합니다.
모든 함수는 함수의 이름을 가지고 있으며, 보통 그 이름은 함수가 수행할 내용을 묘사하도록 작성합니다. 함수를 사용하고 싶다면, 함수의 이름과 함수가 받는 입력값 (매개변수) 를 작성함으로써 호출 할 수 있습니다.
예시는 다음과 같습니다.
func greet(person: String) -> String {
let greeting = "안녕, " + person + "!"
return greeting
}
print(greet(person: "안나"))
// Prints "안녕, 안나!"
print(greet(person: "브라이언"))
// Prints "안녕, 브라이언!"
greet(person:)
함수를 호출하기 위해, 함수의 이름 을 적고, person:
인자 라벨 뒤에 String 값을 전달한 코드를 위에서 확인할 수 있습니다.
Note : print(_:separator:terminator:)
역시 함수인데,
첫번째 인자 (== 매개변수) 에 별 다른 라벨이 붙지 않은 것을 확인할 수 있습니다.
또한 2 번째, 3 번째인자들은 Optional
인 것을 확인할 수 있는데, 이 두 인자는 기본 값 default Value 를 갖기 때문입니다.
이와 같은 함수의 문법들은 하단에서 설명합니다.
Swift 에서 함수의 매개변수와 반환값은 굉장히 자유롭게 사용할 수 있습니다.
func sayHelloWorld() -> String {
return "hello, world"
}
print(sayHelloWorld())
// Prints "hello, world"
func greet(person: String) -> String {
let greeting = "안녕, " + person + "!"
return greeting
}
func greetAgain(person: String) -> String {
return "다시 한번 안녕," + person + "!"
}
func greet(person: String, alreadyGreeted: Bool) -> String {
if alreadyGreeted {
return greetAgain(person: person)
} else {
return greet(person: person)
}
}
print(greet(person: "팀", alreadyGreeted: true))
// Prints "다시 한번 안녕, 팀!"
person
과 alreadyGreeted
라는 2개의 매개변수를 받고서,
if
문에서 alreadyGreeted
매개변수를 통해 분기문을 만들고,
상황에 따라 각기 다른 함수를 호출하는 모습을 확인할 수 있다.
func greet(person: String) {
print("안녕, \(person)!")
}
greet(person: "데이브")
// Prints "안녕, 데이브!"
함수 정의에서 반환 화살표 ->
를 통해 반환 값을 명시하지 않아서,
함수 호출에도 불구하고 함수 자체의 반환값은 없다.
혹은 함수의 리턴값을 의도적으로 무시할 수도 있다.
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" 이며, 아무런 값도 반환하지 않는다.
Note: 함수의 리턴값은 무시될 수 있지만, 함수가 리턴을 정의했다면 함수는 반드시 특정 자료형을 리턴해야한다. 리턴이 정의된 함수가 리턴을 하지 않으면 컴파일 에러가 발생한다.
함수는 여러개의 리턴 값을 하나로 뭉쳐서 리턴할 수 있다.
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)
}
let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("최소는 \(bounds.min) 이고, 최대는 \(bounds.max) 이다.")
// 출력: "최소는 -6 이고, 최대는 109 이다."
이때 bounds 가 tuple
형태의 리턴값을 받았음에도,
별다른 이름, min
, max
, 을 지정하지 않았는데 출력에서는 사용할 수 있는 이유는
함수가 리턴할 때 이미 튜플들의 이름을 정의했기 때문이다.
만약 리턴 값이 튜플 자료형인데, 튜플 자체가 nil
(없음) 에 해당할 수 있다면 tuple 전체에 ?
를 붙임으로써 optional
타입으로 반환할 수 있다.
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)
}
이때 만약 튜플 이상의 자료를 리턴하고 싶다면 (Int, String, Int)
형태로 계속해서 붙여서 사용할 수 있으며, Optional
역시 동일하게 적용된다.
Note: (Int?, Int?)
와 (Int, Int)?
는 다르다. 전자는 각각의 값이 nil
일 수 있다면, 후자는 튜플 전체 자체가 nil
일 수 있음을 명시한 것이다.
만약 함수가 리턴값이 있는데, 함수가 1줄이라면 암시적으로 그 1줄의 내용이 리턴된다.
func greeting(for person: String) -> String {
"안녕, " + person + "!"
}
print(greeting(for: "데이브"))
// Prints "안녕, 데이브!"
func anotherGreeting(for person: String) -> String {
return "또 한번 안녕, " + person + "!"
}
print(anotherGreeting(for: "데이브"))
// Prints "또 한번 안녕, 데이브!"
Shorthand Getter Declaration 에서 프로퍼티의 get 을 통한 암시적 반한을 확인할 수 있다.
Note: 함수가 리턴값이 없는데 암시적 리턴을 사용할 수는 없다.
각 함수의 파라미터는 인자 라벨
과 파라미터 이름
을 동시에 갖고 있다.
라벨은 함수를 호출할 때 사용되며, 파라미터는 함수 내에서 사용할 때 사용한다.
기본적으로 인자 라벨과 파라미터는 동일한 이름을 갖는다.
다음과 같은 방법으로 인자 라벨을 특정할 수 있다.
func someFunction(argumentLabel parameterName: String) {
// 함수 내에서, 파라미터 이름은 parameterName 이 된다.
// 함수 호출 시, 인자 라벨은 argumentLabel 이 된다.
print(parameterName)
}
someFunction(argumentLabel: "그라데이션")
// 출력 : 그라데이션
응용은 다음과 같이 할 수 있다.
func greet(person: String, from hometown: String) -> String {
return "안녕 \(person)! \(hometown)에서 널 만나니 기뻐!"
}
print(greet(person: "빌", from: "쿠퍼티노"))
// Prints "안녕 빌! 쿠퍼티노에서 널 만나니 기뻐!"
만약 라벨이 필요 없다면 _
을 사용해서 생략할 수 있습니다.
func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
// 함수 내에서 firstParameterName 과 secondParameterName 은 호출될 때 받은 1과 2를 지칭합니다.
}
someFunction(1, secondParameterName: 2)
파라미터 자료형 다음에 그 값을 할당해줌으로써, 함수의 어떤 파라미터든 기본값을 정의해줄 수 있습니다.
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
// someFunction 을 호출할 때, 2번째 인자를 생략하면
// 자동적으로 12가 할당되서 사용됩니다.
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault 의 값은 6으로 새롭게 할당됩니다.
someFunction(parameterWithoutDefault: 4) // parameterWithDefault 의 값이 기본값인 12로 할당됩니다.
가변 파라미터는 특정 값을 0개부터 다양한 값을 받을 수 있습니다.
가변 파라미터는 상수 배열로 할당됩니다.
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)
// 5개의 평균인 3이 반환된다.
arithmeticMean(3, 8.25, 18.75)
// 3개의 평균인 10이 반환된다.
함수의 매개변수들은 기본적으로 상수입니다. 함수 내에서 매개변수를 변경하려고 시도하면 컴파일 단계에서 에러를 발생시킵니다. 이는 파라미터를 실수로 변화시키지 못하도록 미연에 방지하는 역할을 합니다.
만약 함수내에서 파라미터의 값을 변경하고 싶으며, 이 변경이 원본 값에도 영향을 미치도록 하고 싶다면 파라미터를 in-out
파라미터로 정의할 수 있습니다.
파라미터 앞에 in-out
파라미터를 정의하여 사용할 수 있습니다. 함수 내에서 in-out
파라미터가 변경되면 함수에서 변경과 함께 원본 값 역시 변경됩니다.
in-out
파라미터는 반드시 변수, var
만 전달할 수 이습니다. in-out
파라미터에는 상수나 리터럴 값을 전달할 수 없는데, 상수나 리터럴은 수정이 불가능하기 때문입니다.
인자로 전달할때는 &
를 붙여서 전달하며, 함수 내에서 이 변수가 수정될 수 있다는 의미를 가집니다.
예시입니다.
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는 \(someInt) 이며, anotherInt는 이제 \(anotherInt) 입니다.")
// Prints "someInt는 이제 107 이며, anotherInt는 이제 now 3"
Note: in-out
파라미터는 함수가 리턴하는 것과 다릅니다. 상단의 예시에서 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
}
위의 함수는 다음과 같이 해석할 수 있습니다.
"함수는 2개의 파라미터를 받는데, 둘 다 Int guddlrh, Int 자료형의 값을 반환하는구만"
다른 예시입니다.
func printHelloWorld() {
print("hello, world")
}
위의 함수는 다음과 같이 해석할 수 있습니다
"함수는 파라미터가 없고, Void 를 리턴하는구만"
함수는 함수내에서도 다른 함수를 호출할 수 있습니다. (블로거: 물론 자기 자신도요 ! )
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!