1편에 이어
[Swift] HOF(Higher Order Function) (1)
Swift 기본 고차함수(HOF; Higher Order Function) 에 대해알아보고 간단히 구현해보자. 고차함수란, 함수를 인자로 전달받거나 함수를 결과로 반환하는 함수를 의미한다. 보통 Swift에서 고차함수라고 한
littlemoom.tistory.com
2편에서는 contains, min, max도 사실 고차함수의 형태였다는 사실과 flatMap, compactMap, allSatisfy, partition 에 대해 알아보자.
contains
func contains(_ element: Self.Element) -> Bool
Sequence안에 Element가 존재하면 true를 반환한다.
하지만 해당 함수는 Element가 Equatable Protocol을 사용할 때만 사용할 수 있다.
만약 Element가 Equatable하지 않다면,
func contains(where predicate: (Self.Element) throws -> Bool) rethrows -> Bool
을 사용하자.
enum HTTPResponse {
case ok
case error(Int)
}
let lastThreeResponses: [HTTPResponse] = [.ok, .ok, .error(404)]
let hadError = lastThreeResponses.contains { element in
if case .error = element {
return true
} else {
return false
}
}
// 'hadError' == true
위와 같은 형태로 사용할 수 있다.
max, min
contain처럼 Element가 Equatable하다면,
func max() -> Self.Element?
func min() -> Self.Element?
단순하게 사용될 수 있다.
Equatable하지 않은 경우에는
func max(by areInIncreasingOrder: (Self.Element, Self.Element) throws -> Bool) rethrows -> Self.Element?
func min(by areInIncreasingOrder: (Self.Element, Self.Element) throws -> Bool) rethrows -> Self.Element?
이 함수들이 호출된다.
당장 Dictionary에 적용해 본다면?
int value가 가장 큰 요소를 찾고 싶다고 가정하자.
let hues = ["Heliotrope": 296, "Coral": 16, "Aquamarine": 156]
let greatestHue = hues.max { a, b in a.value < b.value }
print(greatestHue)
// Prints "Optional((key: "Heliotrope", value: 296))"
만약 고차함수 max가 아닌 일반 max를 사용한다면,
let hues = ["Heliotrope": 296, "Coral": 16, "Aquamarine": 156]
let greatestHue = hues.first { element in
element.value == hues.values.max()
}
print(greatestHue)
// Prints "Optional((key: "Heliotrope", value: 296))"
무엇도 가독성이 좋은지는 개인의 판단에 맡기겠다!
알아두면 1편에서 알아본 고차함수보다도 조금 더 알찬(?) 다양한 고차함수들을 알아보자.
flatMap
// 1차원 Sequence
func flatMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
// 2차원 이상 Sequence
func flatMap<SegmentOfResult>(_ transform: (Self.Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence
1차원 Sequence에서 사용할 떄와 2차원이상의 Sequence에서 사용할 때 호출되는 함수가 다르다.
1차원에서 호출된 함수는 return 값이 nil이면 요소에서 제거한다.
또 옵셔널 바인딩을 해주기 때문에 반환되는 배열은 옵셔널이 아닌 Element의 배열이 된다.
2차원 이상일 때는 차원을 한단계 낮춰서 Sequence를 flatten하게 만들어준다.
let numbers = [1,2,nil,3,4,nil,5,nil]
numbers.flatMap {
return $0
}
// [1, 2, 3, 4, 5]
let numbersArray = [[1, 2, 3],
[5, nil, 0],
[nil, nil, 7]]
numbersArray.flatMap {
return $0
}
// [1, 2, 3, 5, nil, 0, nil, nil, 7]
1차원에서 사용되는 flatMap은 사실 deprecated 되어서, compactMap을 사용해야한다.
compactMap
func compactMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
1차원 Sequence에서 호출된 flatMap과 동일한 역할을 한다.
let numbers = [1,nil,nil,2,nil,3,4,5,nil]
numbers.compactMap {
return $0
}
// [1, 2, 3, 4, 5]
allSatisfy
이 함수를 알게된 것이 글을 쓰게 된 원인이다.
func allSatisfy(_ predicate: (Self.Element) throws -> Bool) rethrows -> Bool
predicate에서 반환되는 Bool 값이 모두 true일 때, 최종적으로 allSatisfy가 true를 반환한다.
let names = ["Sofia", "Camilla", "Martina", "Mateo", "Nicolás"]
let allHaveAtLeastFive = names.allSatisfy({ $0.count >= 5 })
// true
let allHaveAtLeastSix = names.allSatisfy({ $0.count >= 6 })
// false
let emptyArray: [Double] = []
emptyArray.allSatisfy({ $0.count >= 5 })
// true
단, 배열이 비어있다면 true를 반환한다.
partition
func partition(by belongsInSecondPartition: (Self.Element) throws -> Bool) rethrows -> Self.Index
belongsInSecondPartition에서 true가 반환된 Element와 false이 반환된 Element를 구분하고 구분 지점의 index를 반환한다.
allSatisfy만큼이나 엄청난 녀석이다.
var numbers = [30, 40, 20, 30, 30, 60, 10]
let p = numbers.partition(by: { $0 > 30 })
// p == 5
// numbers == [30, 10, 20, 30, 30, 60, 40]
반환된 index부터는 true가 반환된 Element가 된다.
주의할 점은 정렬해주지 않는다! 특정 순서가 전혀 보장되지 않기때문에 조심해야한다.
전부 true 또는 false 라면?
var numbers = [30, 40, 20, 30, 30, 60, 10]
let allTrue = numbers.partition(by: { $0 > 5 })
// 0
let allFalse = numbers.partition(by: { $0 < 5 })
// 7
전부 true일 땐 0이, 전부 false일 땐 last index + 1 (== count) 가 된다.
따라서 전부 false을 경우 바로 subscript index로 사용한다면 에러가 발생할 것이다.
Reference
https://www.swiftbysundell.com/tips/picking-between-for-and-for-each/
'iOS > Swift' 카테고리의 다른 글
[Swift] Method Dispatch (1) | 2024.01.15 |
---|---|
[Swift] Protocol Composition (0) | 2024.01.09 |
[Swift] HOF(Higher Order Function) (1) (0) | 2022.08.03 |
[Swift] await, async (0) | 2022.06.30 |
[Swift] 클래스 (0) | 2022.06.24 |