Swift 고차함수 - ehrldyd15/Swift_Skills GitHub Wiki
고차함수 (Higher-order function)
고차 함수란 매개변수로 함수를 갖는 함수
map(_:)
let cast = ["Vivien", "Marlon", "Kim", "Karl"]
let lowercaseNames = cast.map { $0.lowercased() }
// 'lowercaseNames' == ["vivien", "marlon", "kim", "karl"]
let letterCounts = cast.map { $0.count }
// 'letterCounts' == [6, 6, 3, 4]
호출할떄 매개변수로 전달된 함수를 실행하여 그 결과를 반환한다.
스위프트의 Sequence, Collection 프로토콜을 따르는 타입은 모두 사용 가능하다.
컨테이너가 담고 있던 각각의 값을 매개변수를 통해 받은 함수에 적용 후 다시 컨테이너에 담아서 반환한다.
기존의 컨테이너의 값은 변경되지 않고 새로운 컨테이너가 생성되어 반환된다.
filter(_:)
let cast = ["Vivien", "Marlon", "Kim", "Karl"]
let shortNames = cast.filter { $0.count < 5 }
print(shortNames)
// Prints "["Kim", "Karl"]"
매개변수로 전달되는 함수의 반환타입은 Bool타입이다.
map(_:)처럼 새로운 컨테이너에 값을 담아 반환하지만 기존 컨텐츠를 변형하지 않고 특정 조건에 맞게 걸러내는 역할을 한다.
reduce(_ :_ :)
let numbers = [1, 2, 3, 4]
let numberSum = numbers.reduce(0, { x, y in
x + y
})
// numberSum == 10
컨테이너 내부의 컨텐츠를 하나로 합치는 기능을 실행한다.
배열이라면 배열의 모든 값을 전달인자로 받은 클로저의 연산 결과로 합쳐준다.
스위프트의 리듀스는 두가지 형태인데,
첫번쨰 리듀스는 클로저가 각 요소를 전달받아 연산한 후 값을 다음클로저 실행을 위해 반환하며 컨테이너를 순환
두번째 리듀스는 컨테이너를 순환하며 클로저가 실행되지만, 따로 결과값을 반환하지 않음. 대신 inout 매개변수를 사용하여 초기값에 직점 연산을 실행한다.
compactMap(_:)
let possibleNumbers = ["1", "2", "three", "///4///", "5"]
let mapped: [Int?] = possibleNumbers.map { str in Int(str) }
print(mapped)
// [1, 2, nil, nil, 5]
let compactMapped: [Int] = possibleNumbers.compactMap { str in Int(str) }
print(compactMapped)
// [1, 2, 5]
매개변수로 전달된 클로저가 옵셔널 값을 생산할떄 사용한다.
시퀀스의 각 요소를 전달받은 클로저에 적용하고 nil이 아닌 값들만 배열 추가한 후 반환한다.
flatMap(_:)
let numbers = [1, 2, 3, 4]
let mapped = numbers.map { Array(repeating: $0, count: $0) }
// [1], [2, 2], [3, 3, 3], [4, 4, 4, 4](/ehrldyd15/Swift_Skills/wiki/1],-[2,-2],-[3,-3,-3],-[4,-4,-4,-4)
let flatMapped = numbers.flatMap { Array(repeating: $0, count: $0) }
// [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
시퀀스의 각 요소들에 매개변수로 전달된 클로저를 적용하여 연속적인 값을 가지는 배열을 반환한다.
매개변수 클로저가 각 요소에 대한 시퀀스 혹은 컬렉션을 생산할때, 일차원 컬렉션을 얻고자 사용한다.
flatMap, compactMap 심화
let array = [1, nil, 3, nil, 5, 6, 7]
let flatMapTest = array.flatMap{ $0 }
let compactMapTest = array.compactMap { $0 }
print("flatMapTest :", flatMapTest)
print("compactMapTest :", compactMapTest)
<출력>
flatMapTest : [1, 3, 5, 6, 7]
compactMapTest : [1, 3, 5, 6, 7]
둘 다 동일한 결과가 나온다. (단, 1차원 배열에서만..)
기존의 flatMap의 경우에는 배열을 flatten하게 만들어주며 nil을 제거, 옵셔널 바인딩해주는 역할이다.
위의 코드는 아래의 경고를 유발한다.

즉, Swift 4.1부터는 1차원 배열에서 nil을 제거하고 옵셔널 바인딩을 하고싶을때는 compactMap을 사용하면 된다.
let array: [Int?](/ehrldyd15/Swift_Skills/wiki/Int?) = [1, 2, 3], [nil, 5], [6, nil], [nil, nil](/ehrldyd15/Swift_Skills/wiki/1,-2,-3],-[nil,-5],-[6,-nil],-[nil,-nil)
let flatMapTest = array.flatMap { $0 }
let compactMapTest = array.compactMap { $0 }
print("flatMapTest :",flatMapTest)
print("compactMapTest :",compactMapTest)
<출력>
// flatMapTest : [Optional(1), Optional(2), Optional(3), nil, Optional(5), Optional(6), nil, nil, nil]
// compactMapTest : [Optional(1), Optional(2), Optional(3)], [nil, Optional(5)], [Optional(6), nil], [nil, nil](/ehrldyd15/Swift_Skills/wiki/Optional(1),-Optional(2),-Optional(3)],-[nil,-Optional(5)],-[Optional(6),-nil],-[nil,-nil)
위 코드처럼 2차원 배열일 경우에는 둘 다 nil을 제거하지않고, 1차원 배열일때만 nil을 제거한다.
flatMap은 2차원 배열을 1차원배열로 만들어주는 반면 compactMap은 1차원 배열로 만들지 않는다.
즉, 2차원 배열을 1차원으로 만들때는 flatMap을 쓰면 된다.
let array: [Int?](/ehrldyd15/Swift_Skills/wiki/Int?) = [1, 2, 3], [nil, 5], [6, nil], [nil, nil](/ehrldyd15/Swift_Skills/wiki/1,-2,-3],-[nil,-5],-[6,-nil],-[nil,-nil)
let flatMapTest = array.flatMap { $0 }.compactMap{ $0 }
<출력>
// flatMapTest : [1, 2, 3, 5, 6]
위 코드의 경우 2차원 배열을 flatMap으로 1차원배열로 만들어주고 compactMap으로 체이닝을 하면 원하는 값을 얻을 수 있다.
정리
Swift 4.1부터는 1차원 배열에서 nil을 제거하고 옵셔널 바인딩을 하고싶으실때는 compactMap 사용.
2차원 배열을 1차원 배열로 flatten하게 만들때 flatMap을 사용.