0. 고차함수
고차함수란?
매개변수로 함수를 받는 함수를 말한다. Swift에서 함수는 일급시민이기 때문에 다른 함수를 전달인자로 받을 수 있다.
Swift 표준 라이브러리에서는 다음과 같은 고차 함수를 제공한다
- map
- filter
- reduce
모두 Container *(Array, Set, Dictionary 등)와 *Optional 타입에서 사용할 수 있다.
for-in 구문과 기본적인 작동 원리가 같으나, 다음과 같은 이점이 있다.
- 코드가 간결하다
- 재사용이 쉽다
- 컴파일러 최적화 성능이 좋다
1. Map
map이란?
제공된 클로저를 각 항목에 적용한 후, 원래의 순서와 같도록 배치한 뒤 반환하는 메소드. 기존 데이터를 변형
하여 새로운 컨테이너를 생성하는 것
아래와 같은 for-in을,
let numberList: [Int] = [1, 2, 3, 4]
var returnArray: [Int] = []
for number in numberList {
returnArray.append(number * 2)
}
print(returnArray) //[2, 4, 6, 8]
map으로 쓰면 다음과 같다.
let returnArray = numberList.map({ (number : Int) -> Int in
return number * 2 }) //Int 값을 받아온 뒤 2를 곱하여 반환
비교 포인트
- 초기에 빈 배열을 생성할 필요가 없다
- append() 연산을 수행하지 않아도 되어 시간 절약이 된다
이것저것 생략할 수 있다.
map({ (number : Int) -> Int in number * 2 }) //return 생략. 단, 클로저 내부의 코드가 두 줄 이상이라면 return을 생략할 수 없다
map({ (number) -> Int in number * 2 }) //원 타입 생략
map({ (number) -> in number * 2 }) //반환 타입 생략
map({ $0 * 2 }) //매개변수 생략. 이때 $0은 1번째 매개변수라는 의미
map{ $0 * 2 } //후행 클로저. 괄호 생략 후 클로저만 뒤따르게 된다.
* 하지만 과한 생략 시 가독성이 떨어지므로 주의가 필요하다.
매개변수로 전달할 함수를 클로저 상수로 만들어 코드 재사용이 가능하다.
let firstNumberList: [Int] = [1, 2, 3, 4, 5]
let secondNumberList: [Int] = [5, 6, 7, 8, 9]
let multiplyHundred: (Int) -> Int = { $0 * 100 } //클로저 상수 선언
let calcFirstNumberList = firstNumberList.map(multiplyHundred)
let calcSecondNumberList = secondNumberList.map(multiplyHundred)
2. Filter
filter란?
컨테이너 내부의 값을 걸러서 새로운 컨테이너로 추출
하는 메소드. 반환 타입은 Bool이며, true일 때 값을 포함한다.
아래와 같은 for-in을,
let numberlist: [Int] = [1, 2, 3, 4, 5, 6, 7, 8]
var evenNumbers: [Int] = []
for number in numberlist {
if number % 2 == 0 {
evenNumbers.append(number)
}
}
print(evenNumbers) // [2, 4, 6, 8]
filter로 쓰면 다음과 같다.
let evenNumbers: [Int] = numberlist.filter {(number: Int) -> Bool in
return number % 2 == 0
}
싸그리 생략하면 다음과 같다
let evenNumbers: [Int] = numberlist.filter{ $0 % 2 == 0 }
다음과 같이 map과 filter를 함께 쓸 수도 있다
let ageNow: [Int] = [10, 13, 16, 18, 19]
let adultInFourYears: [Int] = ageNow.map{ $0 + 4 }.filter{ $0 >= 20 }
//map으로 4를 더한 값을 반환한 배열을 만든 뒤, filter로 20 이상의 데이터를 추출하여 다시 배열로 넘김
print(adultInFourYears) //[20, 22, 23]
이번엔 Dictionary를 설정하고 위와 같은 로직을 실행하였다
let ageNow: [String:Int] = ["a":10, "b":13, "c":16, "d":18, "e":19]
let adultInFourYears: [String: Int] = ageNow.mapValues{ $0 + 4 }.filter{ $0.value >= 20 }
print(adultInFourYears) // ["e": 23, "c": 20, "d": 22] (Dictionary이므로 출력 순서는 랜덤)
Dictionary에서 key는 immutable하므로 변형
을 제공하는 map에서는 mapValues
라는 이름의 메소드를 통해 value 값을 변경하는 것만 가능하다.
이에 반해, filter에서는 key와 value 모두 필터링에 따른 추출
이 가능하여 $0.value나 $0.key와 같은 식으로 지칭이 가능하다.
3. Reduce
reduce란?
컨테이너 내부의 데이터를 하나로 통합
하는 메소드
아래의 for-in이,
let numberlist: [Int] = [1, 2, 3, 4, 5, 6, 7, 8]
var sum: Int = 0
for number in numberlist {
sum += number
}
print(sum) //36
reduce를 사용하면 이렇게 된다.
let sum: Int = numberlist.reduce(0, {(result: Int, currentItem: Int) -> return result + currentItem})
//이때 0은 sum의 초깃값이다.
//result는 0에서 출발하여 1(0+1) -> 3(1+2) -> 6(3+3)로 나아가는, 모든 수의 합에 도달하기까지 단계별 결괏값을 의미한다.
//currentItem은 각 순환에서 배열로부터 배당되는 데이터 값이다
축약하면 다음과 같다
let sum = numberlist.reduce(0){ $0 + $1 }
* Swift에서는 연산자도 함수인데, +
함수는 매개변수 두개를 받아서 합을 반환하는 클로저이다. 따라서 아래와 같은 표현도 가능하다.
let sum = numberlist.reduce(0,+)
// '+' = {(result: Int, currentItem: Int) -> return result + currentItem}
참고한 글 목록
'Learnings > Swift & iOS' 카테고리의 다른 글
[iOS] UIScrollView 만들기 (0) | 2021.03.15 |
---|---|
Swift Protocol - CustomStringConvertible 간단 정리 (0) | 2021.02.15 |
Swift 지연실행 실험 - NSTimer, asyncAfter, DispatchSourceTimer (0) | 2021.01.27 |
===와 !==, 참조 비교 연산자 - Swift (1) | 2021.01.13 |
REPL로 swift하기: invalid active developer path error 해결 (0) | 2020.12.20 |
댓글