고차 함수란?
고차 함수(Higher-Order Function)는 하나 이상의 함수를 인자로 받거나, 함수를 결과로 반환하는 함수를 말합니다.
스위프트에서 제공하는 고차함수는 map, filter, reduce 3가지가 있으며, 컬렉션을 처리할 때 매우 유용합니다.
map(변형)
map 메서드는 컨테이너 내부에 각 요소를 변형(transform)하여 새로운 컨테이너를 생성합니다.
다만, 새로운 컨테이너를 생성한 것이기 때문에 기존의 데이터는 변하지 않습니다.
map은 for-in 구문과 유사한 결과를 만들지만, 코드가 더 간결하고 선언적으로 작성할 수 있다는 장점이 있습니다.
그러면, for-in 문과 map 메서드를 사용한 코드를 비교해보겠습니다.
for-in
let numbers: [Int] = [0, 1, 2, 3]
var doubleNumbers: [Int] = []
for number in numbers {
doubleNumbers.append(number * 2)
}
print(doubleNumbers) // [0, 2, 4, 6]
// 새로 생성한 것이기 때문에 numbers의 데이터는 변하지 않음.
print(numbers) // [0, 1, 2, 3]
map
let numbers: [Int] = [0, 1, 2, 3]
let doubleNumbers: [Int] = numbers.map { (number: Int) -> Int in
return number * 2
}
print(doubleNumbers) // [0, 2, 4 ,6]
// 마찬가지로, numbers의 데이터는 변하지 않음.
print(numbers) // [0, 1, 2, 3]
위의 코드처럼, map 메서드를 사용하면 코드가 더욱 간결해지는 걸 볼 수 있습니다.
또한, 위의 코드에서 매개변수 및 반환 타입, 반환 키워드(return)를 생략하고, 후행 클로저를 사용하면 아래와 같이 코드가 더욱 간략해진 걸 볼 수 있습니다.
map 축약
let numbers: [Int] = [0, 1, 2, 3]
let doubleNumbers: [Int] = numbers.map { $0 * 2 }
print(doubleNumbers) // [0, 2, 4, 6]
// 동일하게 numbers의 값은 [0, 1, 2, 3]으로 기존의 데이터는 변하지 않음.
filter(추출)
filter 메서드는 컨테이너 내부의 요소 중 조건을 만족하는 값만 걸러내어, 새로운 컨테이너로 반환합니다.
클로저의 반환 타입은 Bool 타입이며, true를 반환하는 요소만 결과 컨테이너에 포함됩니다.
이번에도 for-in 문과 filter 메서드를 비교해봅시다!
for-in
let numbers: [Int] = [0, 1, 2, 3, 4, 5]
var evenNumbers: [Int] = []
for number in numbers {
if number % 2 == 0 {
evenNumbers.append(number)
}
}
print(evenNumbers) // [0, 2, 4]
print(numbers) // [0 ,1, 2, 3, 4, 5]
filter
let numbers: [Int] = [0, 1, 2, 3, 4, 5]
let evenNumbers: [Int] = numbers.filter { (number: Int) -> Bool in
return number % 2 == 0
}
print(evenNumbers) // [0, 2, 4]
print(numbers) // [0, 1, 2, 3, 4, 5]
이처럼 filter 메서드를 사용하면 for-in 문에 비해 코드가 보기 쉽고 간략화된 것을 볼 수 있습니다.
그리고 filter 또한 아래처럼 축약하여 사용할 수 있습니다.
filter 축약
let numbers: [Int] = [0, 1, 2, 3, 4, 5]
let evenNumbers: [Int] = numbers.filter { $0 % 2 == 0 }
print(evenNumbers) // [0, 2, 4]
print(numbers) // [0, 1, 2, 3, 4, 5]
reduce(결합)
reduce 메서드는 컨테이너 내부의 요소들을 지정한 방식으로 하나의 결과값으로 결합시켜줍니다.
첫 번째 매개변수를 통해 초기값을 지정해줄 수 있으며, 정수 배열이라면 연산 결과를 합치고 문자열 배열이라면 문자열을 하나로 통합하는 일을 해줍니다.
for-in
let numbers: [Int] = [0, 1, 2, 3, 4, 5]
var reduceResult: Int = 0
for number in numbers {
reduceResult += number
}
print("reduceResult: \(reduceResult)") // reduceResult: 15
print(numbers) // [0, 1, 2, 3, 4, 5]
reduce
let numbers: [Int] = [0, 1, 2, 3, 4, 5]
let reduceResult: Int = numbers.reduce(0) {
(result: Int, next: Int) -> Int in
print("\(result) + \(next)")
return result + next
}
print("reduceResult: \(reduceResult)")
// 0 + 0
// 0 + 1
// 1 + 2
// 3 + 3
// 6 + 4
// 10 + 5
// reduceResult: 15
print(numbers)
// [0, 1, 2, 3, 4, 5]
이 부분은 조금 헷갈릴 수 있으니 설명을 더 적어볼게요 😋
numbers 배열에 초기값을 만들어준 후, reduce 메서드 첫 번째 매개변수의 초기값을 0으로 설정해주었으며, 두 번째 매개변수 클로저를 보면 result와 next를 더해서 Int 형태로 반환해주는 코드입니다.
이렇게 되면 numbers배열에 있는 각 요소들은 더해지며 reduceResult 변수에 대입됩니다. (여기서 result는 이전 단계까지의 누적값을, next는 현재 처리중인 요소를 의미합니다.)
만약, 초기값인 numbers.reduce(0)를 numbers.reduce(5)로 변경해 주었을 경우, 초기값이 0이 아닌 5이기 때문에 출력 결과는 아래와 같이 나오게 됩니다.
print("reduceResult: \(reduceResult)")
// 5 + 0 = 5
// 5 + 1 = 6
// 6 + 2 = 8
// 8 + 3 = 11
// 11 + 4 = 15
// 15 + 5 = 20
// reduceResult: 20
print(numbers)
// [0, 1, 2, 3, 4, 5]
또한, reduce도 map과 filter와 마찬가지로 아래와 같이 생략하여 축약 표현이 가능합니다. 👍
reduce 축약
let numbers: [Int] = [0, 1, 2, 3, 4, 5]
let reduceResult: Int = numbers.reduce(0) { $0 + $1 }
print(reduceResult) // 15
print(numbers) // [0, 1, 2, 3, 4, 5]
마지막 정리!
메서드 | 역할 | 반환 값 | 사용 예시 |
map | 각 요소를 변형 | 새로운 컨테이너 | [1, 2, 3].map { $0 * 2 } -> [2, 4, 6] |
filter | 조건에 맞는 요소 추출 | 새로운 컨테이너 | [1, 2, 3].filter { $0 % 2 == 0 } -> [2] |
reduce | 요소들을 하나의 값으로 결합 | 단일 값 (Int, String 등) | [1, 2, 3].reduce(0) { $0 + $1 } -> 6 |
공부한 내용을 바탕으로 정리하다 보니, 오타나 틀린 내용이 있을 수 있습니다.
틀린 내용이나 오타는 댓글로 알려주시면 감사하겠습니다 :)
'📌 Swift' 카테고리의 다른 글
[Swift] 문자열 나누기 split()과 components() 를 알아보자! (0) | 2022.03.29 |
---|---|
[오늘의 Swift 지식] stride 함수 (백준 2742번 기찍 N, 역수 구하기) (0) | 2021.10.24 |
[오늘의 Swift 지식] In-Out이란 무엇일까? (0) | 2021.08.24 |
[오늘의 Swift 지식] 전달인자(Argument)와 매개변수(Parameter) (0) | 2021.08.13 |
[오늘의 Swift 지식] if let과 guard let의 차이는? (0) | 2021.08.09 |