코딩의 꽃이라고 (개인적으로 생각하는) 함수! 함수란 무엇일까? 간단하게 이야기하면 x 가 들어가면 y 가 나오는 것이다. 결국 함수의 목적은 반환값에 있다고 생각한다. 어떠한 논리를 통해 x 를 y 로 바꿔주는 것이 함수이고, Swift 에서는 이를 정의하고, 호출하고 반환값을 사용하게 된다. Swift 에서는 x 를 파라미터라고 부르고 설명한다.
Define and call functions, label their arguments, and use their return values
함수의 정의와 호출 (Defining and Calling Functions)
함수를 정의할 때 들어가는 x 의 타입과 반환되는 y 의 타입을 정의할 수 있다. 또한 그 함수를 사용하려면 함수를 구별할 수 있는 이름을 정해줘야 한다. 아래의 함수는 greet 라는 이름을 가지고 있고, String 을 입력받아 String 값을 반환한다고 선언해 준 함수이다. 이는 func greet(person: String) -> String
으로 표현된다.
func greet(person: String) -> String {
let greeting = "Hello, " + person + "!"
return greeting
}
그렇다면 반환값은? return 으로 표현되어 있다. 바로 greeting 이라는 String 타입의 상수를 반환한다. 그렇다면 이 함수를 어떻게 사용 할 수 있을까? 함수를 불러와서 person 에 String 값을 넣어주면 된다! greet(person: "Anna")
처럼 말이다! 그렇게 되면 위의 함수에 따라 "Hello, Anna!" 가 반환 (return) 된다. 이를 print 로 출력해 보면 아래와 같다.
print(greet(person: "Anna"))
// Prints "Hello, Anna!"
여기서 위의 함수를 좀 더 간결하게 바꾸어 보면, 굳이 greeting 이라는 변수를 쓸 필요가 있을까? 아래와 같이 바로 반환값을 String 타입으로만 표현해 주면 된다.
func greetAgain(person: String) -> String {
return "Hello again, " + person + "!"
}
함수 파라미터와 반환값 (Function Parameters and Return Values)
파라미터 없는 함수 (Functions Without Parameters)
위에서 함수는 x 가 들어가면 y 가 반환된다고 했다. 하지만 Swift 에서는 x 에 아무 값을 넣지 않아도 y 를 반환해 줄 수 있다. 사실 x 와 관계없이 반환되는 y 가 일정하다면 x 에 값을 넣을 필요가 없는 것으로 이해하는 것이 더 좋을 것 같다. 아래와 같이 넣어주는 x 값과 상관없이 "hello, world" 를 반환해 주는 sayHelloWorld 같은 함수가 있다. 함수를 불러올 때 빈 소괄호 sayHelloWorld()
를 붙인다.
func sayHelloWorld() -> String {
return "hello, world"
}
print(sayHelloWorld())
// Prints "hello, world"
여러 개의 파라미터가 있는 함수 (Functions with Multiple Parameters)
Swift 에서 함수는 여러 개의 x 를 받을 수 있다. 바로 아래처럼 이미 인사한 사람 (true) 면 "Hello again, Tim!" 을 반환해 주는 함수를 예시로 들 수 있다.
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!"
반환값이 없는 함수 (Functions Without Return Values)
반환되는 y 의 값이 없이 어떠한 행동을 하는 함수도 있다. 아래처럼 person 에 이름을 입력하면 값을 반환해주는 것이 아니라 "Hello, person!" 을 출력해 주는 함수도 있다. 이 함수는 엄밀히 말하면 Void 타입의 값을 반환해준다고 할 수 있다. -> Void
는 생략된다.
func greet(person: String) {
print("Hello, \(person)!")
}
greet(person: "Dave")
// Prints "Hello, Dave!"
여러 개의 반환값이 있는 함수 (Functions with Multiple Return Values)
반환되는 y 도 여러 개의 반환값이 있을 수 있다. 아래는 배열을 입력해 줘서 max 와 min 값을 튜플형식으로 반환해 준다.
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)
}
이 함수를 어떻게 사용하냐면 함수에 아래처럼 배열을 넣어주고 bounds.min, bounds.max 를 불러와주면 된다.
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"
옵셔널 튜플 반환 타입 (Optional Tuple Return Types)
드디어 복잡해지기 시작했다. 함수에서 반환되는 값이 없을 가능성이 있는 경우 옵셔널 튜플 반환타입을 사용한다. 아래의 경우에는 입력된 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 함수가 실제 값을 반환했는지, nil 을 반환했는지 알기 위해서 if let 을 사용해 줄 수 있다. 이를 옵셔널 바인딩이라고 하는데, 우선 이 정도만 알아두자.
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"
암시적 반환을 가진 함수 (Functions With an Implicit Return)
함수의 반환값이 명확하고 간단하다면 return 을 생략해 줄 수 있다. 아래의 두 함수는 반환값이 같다.
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!"
함수 인수 라벨과 파라미터 이름 (Function Argument Labels and Parameter Names)
함수는 인수 라벨과 파라미터 이름을 가지고 있다. 보통은 파라미터 이름이 인수라벨과 같다.
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.
}
아래처럼 첫번째 파라미터는 파라미터 이름과 인수 라벨이 person 으로 동일하지만 두 번째 파라미터는 인수 라벨이 from, 파라미터 이름이 hometown 이다.
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."
인수 라벨을 활용한다면 문장과 같은 표현방식으로 함수를 사용할 수 있다. 하지만 개인적인 생각으로는 두 개를 구분한다면 코드의 가독성 부분에서 문제가 생길 것 같다...
인수 라벨 생략 (Omitting Argument Labels)
파라미터가 인수라벨을 가지고 있을 경우 함수를 호출할 때 인수라벨을 같이 써야 하지만 _ 로 생략해 준다면 인수라벨을 생략해 줄 수 있다. 우리가 평소에 무심코 쓰고 있던 함수들이 인수라벨이 생략된 경우가 많다. padding(10) 처럼 말이다. 아래에도 첫 번째 파라미터를 넣어 줄 때 그냥 숫자만 넣어줬다.
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)
파라미터 기본값 (Default Parameter Values)
파라미터에 기본값을 넣어 줄 수 있다. 아래처럼 두 번째 파라미터의 기본값은 12 이기 때문에 따로 값을 넣어주지 않는다면 12 로 지정된다.
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 Parameters)
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)
// 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 Parameters)
함수 파라미터는 기본적으로 상수이다. 하지만 파라미터 자체를 변경해주고 싶다면 in-out 을 통해 변경된 값을 파라미터에 적용할 수 있다.
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
이런 식으로 a 와 b 의 값을 변경해 주는 함수를 만들어 줄 수 있다. 아래처럼 someInt 와 anotherInt 의 값을 바꿔준다. in-out 함수를 사용할 때 파라미터 앞에 & 를 붙여야 한다.
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"
이러한 함수는 반환되는 내용이 있지만, 함수의 본분에서 벗어났다고 Swift 공식문서에서는 표현했다.
함수 타입 (Function Types)
함수도 타입이 있다. 바로 파라미터 타입과 반환 타입으로 구성되어 있다. 아래의 두 함수는 (Int, Int) -> Int
타입으로 볼 수 있다.
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
return a * b
}
그렇다면 함수는 아래처럼 mathFunction 이라는 상수, 변수로 정의할 수 있다. 같은 타입을 가지고 있기 때문에 문제없다.
var mathFunction: (Int, Int) -> Int = addTwoInts
그렇게 되면 아래처럼 같은 mathFunction 변수로 필요에 따라 두 가지 함수를 호출할 수 있다.
var mathFunction: (Int, Int) -> Int = addTwoInts
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 5"
mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 6"
파라미터 타입으로 쓰는 함수 타입 (Function Types as Parameter Types)
아래처럼 다른 함수의 파라미터로 함수를 넣어줄 수 있다. 함수 안에서 다른 함수를 써주는 것으로 이해하면 된다.
func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// Prints "Result: 8"
반환 타입의 함수타입 (Function Types as Return Types)
아래의 stepForward, stepBackward 두 가지 함수가 있다고 해보자. 두 함수 모두 (Int) -> Int
타입이다.
func stepForward(_ input: Int) -> Int {
return input + 1
}
func stepBackward(_ input: Int) -> Int {
return input - 1
}
아래의 chooseStepFunction 함수를 보면 함수를 반환한다. backward 가 true 면 stepBackward 를 false 면 stepForward 를 반환한다.
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
return backward ? stepBackward : stepForward
}
아래처럼 currentValue 가 0 보다 크므로 moveNearerToZero 는 stepBackward 함수가 된다.
var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function
중첩 함수 (Nested Functions)
함수 내에서 다른 함수를 정의할 수 있다. 아래와 같이 함수 안에서 다른 함수를 사용하여 좀 더 복잡한 일을 할 수 있게 만들어 준다.
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
이제 moveNearerToZero 는 아래와 같이 쓸 수 있다.
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!
뭐든지 많이 써봐야 느는 것 같다. 이러한 여러 가지 종류들의 함수를 자주 사용하면서 익히도록 노력해야겠다.
'Swift > Swift 공식 문서' 카테고리의 다른 글
[Swift] 제어 흐름 (Control Flow) (1) | 2023.05.19 |
---|---|
[Swift] 콜렉션 타입 (Collection Types) (1) | 2023.05.19 |
[Swift] 문자열과 문자 (Strings and Characters) (1) | 2023.05.18 |
[Swift] 기본연산자 (Basic Operators) (0) | 2023.05.18 |
[Swift] 구조체와 클래스 (Structures and Classes) (0) | 2023.04.30 |