스위프트 프로그래밍 3판 (야곰) 을 보고 정리한 글입니다.
고차함수 : 매개변수로 함수를 갖는 함수.
특히 컬렉션(배열/리스트..등)을 탐색/비교/찾아서 정리하는 기능을 자주 사용한다.
대표적인 고차함수가 map, filter, reduce 이다.

함수형 프로그래밍의 장점을 간편하게 이용할 수 있기 때문.
: 자신을 호출할 때 매개변수로 전달된 함수를 실행하여, 그 결과를 다시 반환해주는 함수.
즉, 기존 데이터를 변형하는 데 사용한다.
컨테이너가 담은 각각의 값을, 매개변수로 받은 함수에 적용한 후, 다시 컨테이너에 포장해 반환.
이 때 기존 컨테이너 값에 영향 없이, 새 컨테이너를 만들어 반환한다.
func map<T>(_ transform: @escaping (Self.Output) -> T) -> Publishers.Map<Self, T>
let numbers = [1, 2, 3]
var doubledNumbers = Array<Int>()
// for-in
for number in numbers {
doubledNumbers.append(number * 2)
}
// map
doubledNumbers = numbers.map({ (number: Int) -> Int in
return number * 2
}) // 기본 클로저
doubledNumbers = numbers.map({ return $0 * 2 }) // 파라미터 타입, 리턴타입 생략
doubledNumbers = numbers.map({ $0 * 2 }) // return 키워드 생략
doubledNumbers = numbers.map { $0 * 2 } // 후행 클로저 사용
let multiplyTwo: (Int) -> Int = { $0 * 2 }
doubledNumbers = numbers.map(multiplyTwo) // 재사용성을 위해 클로저를 상수로 선언
let alphabetDict: [String : String] = ["a":"A", "b":"B"]
var keys = Array<String>()
keys = alphabetDict.map { (tuple: (String, String)) -> String in
return tuple.0 // tuple의 0번째 = key
}
keys = alphabetDict.map { $0.0 } // ["a", "b"]
var values = Array<String>()
values = alphabetDict.map { $0.1 } // ["A", "B"]
var numberSet: Set<Int> = [1, 2, 3]
let resultSet = numberSet.map{ $0 * 2 } // [2, 4, 6]
let range = 0...3 // CountableClosedRange
let resultRange: [Int] = range.map{ $0 * 2 } // [0, 2, 4, 6]
컨테이너 값을 특정 조건에 맞게 걸러내는 고차함수.
func filter(_ isIncluded: @escaping (Self.Output) -> Bool) -> Publishers.Filter<Self>
let numbers = [1, 2, 3]
let evenNumbers: [Int] = numbers.filter { (number: Int) -> Bool in
return number % 2 == 0
}
print(evenNumbers) // [2]
let oddNumbers: [Int] = numbers.filter { $0 % 2 == 1}
print(oddNumbers) // [1, 3]
: 컨테이너 내의 모든 값을 하나로 합하는 기능.
전달인자로 받은 클로저의 연산 결과로 합한다.
상황에 따라 리듀스를 맵과 비슷하게 쓸 수 있다.
reduce(_:_:)public func reduce<T>(_ initialResult: T, _ nextPartialResult: (Result, Element) throew -> Result) rethrows -> Result
클로저가 각 요소를 전달받아 연산한 후, 값을 다음 클로저 실행을 위해 반환하며 컨테이너를 순환하는 형태
initialResult : 초기값nextPartialResult : 클로저. 두 개의 매개변수를 받는다Result) : initialResult로 전달받은 초기값 또는 이전 클로저의 결과값. 순회가 끝났을 때 리듀스의 최종 결과값이 된다Element) : 리듀스 메서드가 순환하는 컨테이너의 요소reduce(_:_:) 예제let numbers: [Int] = [1, 2, 3]
// 초기값 0, numbers의 모든 값을 더함
let sum: Int = numbers.reduce(0, {(result: Int, next: Int) -> Int in
print("\(result) + \(next)")
// 0 + 1
// 1 + 2
// 3 + 3
return result + next
})
print(sum) // 6
// 초기값 3, numbers의 모든 값을 더함
let sumFromThree: Int = numbers.reduce(3) {
print("\($0) + \($1)")
// 3 + 1
// 4 + 2
// 6 + 3
return $0 + $1
}
print(sumFromThree) // 9
// reduce(_:_:) 로 문자열 배열을 연결하기
let names: [String] = ["Tom", "Jerry"]
let reducedNames: String = names.reduce("They are ") {
return $0 + " and " + $1
}
print(reducedNames) // "They are Tom and Jerry"
reduce(into:_:)public func reduce<Result>(into initialResult: Result, _ updateAccumulatingResult: (inout Result, Element) throws -> ()) rethrows -> Result
컨테이너를 순환하며 클로저가 실행되지만, 클로저가 따로 결과값을 반환하지 않는 형태
return 없이, inout 매개변수를 사용해 초기값에 직접 연산을 실행
initialResult : 초기값updateAccumulatingResult : 클로저. 두 개의 매개변수를 받는다inout Result) : inout 매개변수 - initialResult로 전달받은 초기값 또는 이전 클로저에 의해 변경된 결과값. 순회가 끝났을 때 리듀스의 최종 결과값이 된다Element) : 리듀스 메서드가 순환하는 컨테이너의 요소reduce(into:_:) 예제let numbers = [1, 2, 3]
// 초기값이 0이고, numbers의 모든 값을 더한다
// reduce(_:_:)과의 차이 : 클로저 값의 return 없이, 클로저 내부에서 직접 이전 값을 변경함
sum = numbers.reduce(into: 0, { (result: inout Int, next: Int) in
print("\(result) + \(next)")
// 0 + 1
// 1 + 2
// 3 + 3
result += next
})
print(sum) // 6
reduce(::)와 달리 다른 컨테이너에 값을 변경해 넣어줄 수 있다.
맵, 필터와 유사하게 사용이 가능하다.
// 이름을 모두 대문자로 변환해 초기값인 빈 배열에 직접 연산
let names: [String] = ["bibi", "coco", "ruru"]
var uppercasedNames: [String]
uppercasedNames = names.reduce(into: [], {
print("$n : \($0), $n+1 : \($1)")
// $n : [], $n+1 : bibi
// $n : ["BIBI"], $n+1 : coco
// $n : ["BIBI", "COCO"], $n+1 : ruru
$0.append($1.uppercased())
})
print(uppercasedNames) // ["BIBI", "COCO", "RURU"]
// 맵을 사용한 모습
uppercasedNames = names.map { $0.uppercased() }
let numbers = [0, 1, 2, 3, 4, 5]
let mappedNumbers: [Int] = numbers.map { $0 + 3 } // [0, 4, 5, 6, 7, 8]
let evenNumbers: [Int] = mappedNumbers.filter { (number: Int) -> Bool in
return number % 2 == 0
}
print(evenNumbers) // [4, 6, 8]
// mappedNumbers 없이 체이닝도 가능
let evenNumbers: [Int] = numbers.map{ $0 + 3 }.filter{ $0 % 2 == 0}
let numbers = [1, 2, 3, 4, 5, 6, 7]
// 짝수를 걸러내고, 3을 곱한 뒤, 모든 값 더하기
var result: Int = numbers.filter{ $0.isMultiple(of: 2) }.map{ $0 * 3 }.reduce(0){ $0 + $1 } // 2, 4, 6 -> 6, 12, 18 -> 36
print(result) // 36