1. 함수
함수란 특정 작업을 수행하는 코드 모음을 말한다. 함수의 이름을 지을 때는 함수의 동작에 관련되도록 짓는다. 예를 들어 더하는 기능을 하는 함수의 이름은 "add"라고 할 수 있다.
1-1. 함수의 정의
함수는 하나 이상의 매개변수와 반환값을 가질 수 있다. 매개변수(parameter)는 없을 수도 있으며, 반환 값이 Void 타입인 경우 생략이 가능하다.
func 함수명(매개변수명: 타입) -> 반환 타입 {
...함수에서 실행할 작업...
return 반환값
}
1-2. 매개변수(parameter)
매개변수란 함수가 특정 동작을 수행할 때 사용할 값으로, "매개변수명: 타입" 형태로 사용한다. 매개변수명은 목적에 맞게 설정하도록 하며, 설정한 매개변수명은 함수 호출 시 사용해야 한다.
func hello(name: String) -> String {
return "Hello, \(name)!"
}
print(hello(name: "Semin")) // Hello, Semin!
1-2-1. 매개변수는 상수(값 변경 불가능)
또한 매개변수는 상수로써 값을 변경하는 것이 불가능하다. 만약 값을 변경하려 한다면 오류가 발생하므로 이를 위해선 새로운 변수에 값을 저장해야 한다.
func hello(name: String) -> String {
name = "Park" // Cannot assign to value: 'name' is a 'let' constant
var name = name // 사용을 위해선 새로운 변수에 저장해야 함
}
1-2-2. 매개변수는 없을 수도 있다
매개변수가 없다면 괄호는 비워두면 된다.
func hello() -> String {
return "Hello,"
}
print("\(hello()) Semin!" // Hello, Semin!
1-2-3. 매개변수는 여러 개 존재할 수 있다
매개변수가 여러 개인 경우 쉼표(,)로 구분하여 적으면 된다.
func add(a: Int, b: Int) -> Int {
return a+b
}
print(add(a: 10, b: 5)) // 15
1-2-4. 매개변수는 기본값을 가질 수 있다
매개변수는 기본값을 가질 수 있다. "매개변수명: 타입 = 기본값"의 형태로 사용한다. 매개변수에 기본값이 존재하는 경우 함수를 호출할 때 인수(argument)를 생략할 수 있다.
func hello(name: String = "Swift") -> String {
return "Hello, \(name)!"
}
// 매개변수에 기본값이 존재하므로 인수 생략 가능
print(hello()) // Hello, Swift!
1-2-5. 가변 매개변수(Variadic Parameters) - 전체 매개변수의 개수를 모를 때 사용
가변 매개변수란 말 그대로 변하는 매개변수다. 만약 매개변수를 입력받긴 하는데 함수가 호출될 때마다 입력받는 매개변수의 개수가 다를 때는 가변 매개변수를 사용할 수 있다. 가변 매개변수는 매개변수의 타입 뒤에 ...를 추가하여 나타낸다.
func sum(numbers: Int...) -> Int {
numbers.reduce(0, +) // 전체 요소의 합을 반환
}
print(sum(numbers: 1, 2, 3)) // 6
print(sum(numbers: 100, 200)) // 300
1-2-6. In-Out 매개변수 - 매개변수 값을 함수 밖에서도 사용하고 싶을 때 사용
앞서 말한 것처럼 매개변수는 상수로써 값을 변경할 수도 없고 함수 범위 밖에서 사용할 수도 없다. 만약 함수 범위 밖에 있는 값을 인수로 사용하고, 함수의 호출이 종료된 후에도 그 값을 계속해서 사용하고 싶다면 In-Out 매개변수를 사용할 수 있다. 이를 사용하기 위해서는 함수의 선언에서 매개변수의 타입 앞에 inout 키워드를 작성해야 한다. 또한 함수 호출 시 인수 앞에 앰퍼샌드(&)를 작성해야 한다.
func changeValue(a: inout Int, b: inout Int) { // 매개변수 타입 앞에 inout 키워드 사용
(a, b) = (b, a) // a, b의 값을 변경
}
var a: Int = 10
var b: Int = 20
print("함수 호출 전 >> a: \(a), b: \(b)") // 함수 호출 전 >> a: 10, b: 20
changeValue(a: &a, b: &b) // 인수 앞에 & 사용
print("함수 호출 후 >> a: \(a), b: \(b)") // 함수 호출 후 >> a: 20, b: 10
1-2-7. 인수 라벨(Argument Labels)
매개변수는 매개변수 이름을 갖는다고 했다. 그러나 매번 함수를 호출할 때마다 매개변수 이름을 붙이는 것은 번거롭다. 이를 위해 인수 라벨을 사용할 수 있다. 인수 라벨은 함수의 선언에서 매개변수명 앞에 언더바(_)를 작성하면 된다. 이렇게 되면 함수를 호출할 땐 매개변수명을 생략할 수 있고, 함수 내부에서는 매개변수명을 통해 매개변수를 사용할 수 있다.
func add(_ a: Int, _ b: Int) -> Int { // "_ 매개변수명: 타입" 형태로 사용
a+b
}
print(add(10, 5)) // 15
만약 함수를 호출할 때와 함수 내부에서 사용할 때의 매개변수 이름을 달리하고 싶다면 인수 라벨과 매개변수명을 따로따로 작성하면 된다.
func add(first a: Int, second b: Int) -> Int { // 인수 라벨: first, second
a+b // 매개변수명: a, b
}
// 인수 라벨 사용
print(add(first: 10, second: 5)) // 15
1-2-8. 인수 라벨(_)과 매개변수 기본값을 같이 사용하는 경우
매개변수가 여러 개일 때 모든 매개변수에 인수 라벨로 언더바(_)를 사용하고, 몇몇 매개변수만 기본값을 가진다면 기본값을 갖는 매개변수는 기본값을 갖지 않는 매개변수보다 뒤에 작성되어야 한다. 매개변수가 기본값을 가지는 경우 인수를 생략할 수 있고, 인수 라벨을 언더바(_)로 하는 경우 인수명을 생략할 수 있다고 했다.
다음 함수는 두 정수를 입력받아 합을 반환하는 함수이다. 모두 인수 라벨로 언더바(_)를 사용하였고 그중 앞에 있는 매개변수 a는 기본값 10을 갖는다.
func add(_ a: Int = 10, _ b: Int) -> Int {
a+b
}
위 함수를 호출해보자. 이때 인수를 2개 전달하면 함수는 오류 없이 호출된다.
func add(_ a: Int = 10, _ b: Int) -> Int {
a+b
}
print(add(2, 3)) // 5
그러나 만약 인수를 1개 전달하면 어떻게 될까. 이 인수는 기본값을 갖는 매개변수 a에 전달될 것이고 매개변수 b에는 아무런 값이 전달되지 않는다. 따라서 다음과 같은 호출은 오류가 발생한다.
print(add(20)) // Missing argument for parameter #2 in call
1-3. 반환 타입
함수는 반환 값을 갖는다. 반환 값의 타입을 함수 선언에 명시해야 하는데 만약 반환 값이 없는 경우라면, 즉 반환 타입이 Void라면 생략이 가능하다.
정확히 하자면 Void 타입 함수는 반환값이 없는 것은 아니고, ()라는 빈 튜플을 반환한다.
func add(a: Int, b: Int) -> Int { // 반환 타입: Int
return a+b
}
func hello(name: String) -> String { // 반환 타입: String
return "Hello, \(name)!"
}
func start() { // 반환 타입: Void
print("START!")
}
1-3-1. 반드시 return
함수의 반환 타입이 Void 일 때를 제외하고는 반드시 return을 해줘야 한다. 그렇지 않으면 오류가 발생한다.
func hello(name: String) -> String {
print("hello() function")
// Cannot convert return expression of type '()' to return type 'String'
}
1-3-2. 반환 값은 여러 개일 수 있다
함수의 반환 값은 여러 개일 수 있다. 이때 반환되는 값들은 튜플 타입으로 반환된다. 다음 함수는 두 수에 대한 덧셈, 뺄셈 연산의 결과를 반환한다. 반환된 값은 반환 타입에 명시한 튜플 요소의 이름으로 접근할 수 있다.
func addSub(a: Int, b: Int) -> (add: Int, sub: Int) {
return (a+b, a-b)
}
let result = addSub(a: 5, b: 3)
print(result) // (add: 8, sub: 2)
print(result.add, result.sub) // 8 2
1-3-3. return문은 생략할 수 있다
함수의 전체 본문이 한 줄만 존재할 땐 return 키워드를 생략할 수 있다. 함수는 반드시 어떤 값을 반환해야 하는데 전체 본문이 한 줄인 경우 이것은 당연히 반환하는 값을 나타내기 때문이다.
func add1(a: Int, b: Int) -> Int {
return a+b
}
func add2(a: Int, b: Int) -> Int {
a+b // 이렇게 return 키워드를 생략하고 사용할 수 있다.
}
print(add1(a: 5, b: 3)) // 8
print(add2(a: 5, b: 3)) // 8
1-4. 함수 타입(Function Types)
함수 타입이란 (Int) -> Int처럼 매개변수의 타입과 반환 타입이 나타나는 것을 말한다. 함수 타입을 사용하여 변수에 함수를 저장할 수 있다.
다음 함수는 두 정수를 입력받아 합(정수형)을 반환한다. 즉, (Int, Int) -> Int 함수 타입을 갖는다.
func add(_ a: Int, _ b: Int) -> Int {
a+b
}
let myFunc: (Int, Int) -> Int = add // 함수 타입 (Int, Int) -> Int
print(myFunc(1, 2)) // myFunc 함수를 add 함수로 사용
// 3
1-4-1. 매개변수 타입으로 함수 타입을 사용할 수 있다
매개변수 타입으로 함수 타입을 사용할 수 있다. 이말은 즉, 함수의 인수로 함수를 전달할 수 있다는 것이다.
func add(_ a: Int, _ b: Int) -> Int {
a+b
}
func doFunc(_ myFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) -> Int {
myFunction(a, b)
}
print(doFunc(add, 3, 5)) // 8
1-4-2. 함수의 반환 타입으로도 함수 타입을 사용할 수 있다.
함수의 반환 타입으로도 함수 타입을 사용할 수 있다. 즉, 함수의 실행 결과로 함수를 반환할 수 있다는 것이다.
func add(_ a: Int, _ b: Int) -> Int {
a+b
}
func sub(_ a: Int, _ b: Int) -> Int {
a-b
}
// 반환 타입이 (Int, Int) -> Int 인 함수
func returnFunction(_ functionName: String) -> (Int, Int) -> Int {
return functionName == "add" ? add : sub
}
let myFunc: (Int, Int) -> Int = returnFunction("add") // add 함수 -> myFunc
print(myFunc(10, 20)) // 30
1-5. 중첩 함수(Nested Functions)
코드의 특정 범위에 선언되지 않은 함수는 전역 함수(global functions)라고 한다. 함수 내부에 선언된 함수는 중첩 함수(nested functions)라고 한다.
// add, sub 함수는 returnFunction 함수의 중첩 함수
func returnFunction(_ functionName: String) -> (Int, Int) -> Int {
func add(_ a: Int, _ b: Int) -> Int { a+b }
func sub(_ a: Int, _ b: Int) -> Int { a-b }
return functionName == "add" ? add : sub
}
let myFunc: (Int, Int) -> Int = returnFunction("sub") // sub 함수 -> myFunc
print(myFunc(5, 3)) // 2
함수는 실제 개발 시 필연적으로 사용된다.
기능에 따라 함수를 만들어 사용하는 것은 너무나도 당연한 일이므로 다양한 함수를 만들어보고 사용해보며 익히도록 하자.
끝!
'[Programming Language] > [Swift]' 카테고리의 다른 글
[Swift] 선언형 프로그래밍 vs 명령형 프로그래밍 (0) | 2024.08.20 |
---|---|
[Swift] 열거형(enum: Enumerations) 총 정리 (3) | 2024.07.22 |
[Swift] 코딩 테스트에서 자주 쓰이는 max, min, zip, filter, reduce (1) | 2024.07.13 |
[Swift] split과 components의 차이점 (0) | 2024.07.12 |
[Swift] 반복문(for~in, stride, while, repeat~while), 조건문(if, if~else if ~else, defer, switch, fallthrough, binding, where, guard), break, continue, defer, 라벨(label) (1) | 2024.07.08 |