# 고차함수, map, compactMap, flatMap, reduce
## 고차함수
> 정의: 함수를 매개변수로 사용하거나 함수실행의 결과를 함수로 반환하는 함수
내 이해로는 **함수자체를 값처럼 취급**해 함수의 인자(리턴값, 매개변수)로 사용한다.
<img width = 300, src = "https://i.imgur.com/0XI7xaB.png">
**가능한이유?**
스위프트의 함수(클로저)는 **일급시민**이기 때문에 함수의 전달인자로 전달할 수 있으며 함수의 결과값으로 반환할 수 있습니다.
💡 **1급시민이란?** (First-class citizen)
아래 세가지가 가능한 것.
- ✅ 변수에 저장이 가능한가 ?
- ✅ 매개변수로 전달이 가능한가 ?
- ✅ 리턴값으로 사용가능한가?
- ✔️ 변수에 함수를 저장하는 예시
```swift=
func hello() -> String {
return "hello"
}
var A = hello()
print(A) // "hello"
```
- ✔️ 함수를 매개변수로 전달하는 예시
```swift=
func Say(something: String) -> String {
return something
}
print(Say(something: hello())) // "hello"
```
- ✔️ 결과를 함수로 리턴하는 예시
```swift=
func sayFunction() -> String {
return hello()
}
print(sayFunction()) //"hello"
```
- 고차함수의 예시
- map, compactMap, FlatMap,
- Reduce
- forEach
---
## map
- 컨테이너 내부의 데이터를 변형하여 새로운 컨테이너를 생성한다.
- 스위프트의 컨테이너란 `array, set, dictionary` 등을 의미한다.

```swift
//Array 활용
let array = [1, 2, 3, 4, 5]
print(array.map { $0 * 2 })
print(array.map { "Number: \($0)" })
//Set 활용
let set: Set<Int> = [1,2,3,4,4]
print(set.map { $0 * 3 })
//Dictionary 활용
let menus: [String: Int] = ["아메리카노": 4000, "라떼": 4500, "바닐라라떼": 5000, "딸기라떼": 5000]
let menuInfo = menus.map { "\($0)의 가격은 \($1)원 입니다." }
menuInfo.forEach { print($0) }
//String 활용
let letters = "abcdefghijklmnop"
let upperCased = letters.map { $0.uppercased() }
print(upperCased)
print(upperCased.joined())
//숫자를 영단어로 바꾸는 예시
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [235, 38, 827, 47, 60]
print(numbers.map { number -> String in
var number = number
var result = ""
while number > 0 {
result = digitNames[number % 10]! + result
number /= 10
}
return result
})
```
---
## Reduce
- 내부의 값을 결합하여 새로운 값을 만들어 준다.
- 스위프트 GitHub 상 공개 되어 있는 Reduce의 구조는 다음과 같다.
```swift
@inlinable
public func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (_ partialResult: Result, Element) throws -> Result) rethrows -> Result {
var accumulator = initialResult
for element in self {
accumulator = try nextPartialResult(accumulator, element)
}
return accumulator
}
```
- 위와 같은 주는 기본 값을 가지고, 두개의 인자를 받아서 Result 타입으로 변환하여 반환하는 구조를 가지고 있다.
- 위와 같은 구조에서 일반적으로 알지 못하는 타입을 아래와 같이 바꿔서 생각해볼 수 있다.
```swift
extension Array where Element == Int {
func myReduce<Int>(_ initialResult: Int, _ nextPartialResult: (Int, Int) throws -> Int) rethrows -> Int {
var result: Int = initialResult
for value in self {
if let value = value as? Int {
result = try nextPartialResult(result, value)
}
}
return result
}
}
```
- 다음과 같은 함수로 아래의 값을 실행하게 되면 모두 동일하게 1~10까지 더한 수가 나오는 것을 확인 할 수 있다.
```swift
let nums: [Int] = Array(1...10) // 1 ~ 10 모두 더한 값이
let sum = nums.myReduce(0) { partialResult, value in
return partialResult + value
}
let sum2 = nums.myReduce(0) { return $0 + $1 } //클로저 축약
let sum3 = nums.myReduce(0) { $0 + $1 } // return 구문 축약
let sum4 = nums.myReduce(0, +) // + 함수로 클로저 타입 충족
print(sum) // 55
print(sum2) // 55
print(sum3) // 55
print(sum4) // 55
```
---
## compactMap
- map + 옵셔널 제거
- 기존 배열 등의 각 아이템을 새롭게 매핑해서(매핑방식은 클로저가 제공)
변형하되, 옵셔널 요소는 제거하고, 새로운 배열을 리턴
- 옵셔널 바인딩의 기능까지 내장
```swift
let stringArray: [String?] = ["A", nil, "B", nil, "C"]
var newStringArray = stringArray.compactMap { $0 }
print(newStringArray)
//["A", "B", "C"]
let numbers = [-2, -1, 0, 1, 2]
var positiveNumbers = numbers.compactMap { $0 >= 0 ? $0 : nil }
print(positiveNumbers)
//[0, 1, 2]
```
---
## flatMap
- 중첩된 배열의 각 배열을 새롭게 매핑한다.
- 내부 중첩된 배열을 제거하고 리턴
```swift
var nestedArray1: [[Int]] = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 3]]
print(nestedArray1.flatMap { $0 })
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 3]
var nestedArray2: [[Int?]] = [[1, 2, 3], [nil], [4, 5, 6], [7, 8, 9], [1, nil, 3]]
print(nestedArray2.flatMap { $0 })
// [Optional(1), Optional(2), Optional(3), nil, Optional(4),
// Optional(5), Optional(6), Optional(7), Optional(8),
// Optional(9), Optional(1), nil, Optional(3)]
var nestedArray3: [[Int?]?] = [[1, 2, 3], nil ,[nil] , [4, 5, 6], [7, 8, 9], [1, nil, 3]]
print(nestedArray3.flatMap { $0 })
// [[Optional(1), Optional(2), Optional(3)], [nil],
// [Optional(4), Optional(5), Optional(6)],
// [Optional(7), Optional(8), Optional(9)],
// [Optional(1), nil, Optional(3)]]
print(nestedArray3.flatMap { $0 }.flatMap { $0 })
// [Optional(1), Optional(2), Optional(3), nil, Optional(4),
// Optional(5), Optional(6), Optional(7), Optional(8),
// Optional(9), Optional(1), nil, Optional(3)]
var newNnestedArray = [[[1,2,3], [4,5,6], [7, 8, 9]], [[10, 11], [12, 13, 14]]]
var numbersArray = newNnestedArray
.flatMap { $0 }
.flatMap { $0 }
print(numbersArray)
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
```
---
# 고차함수의 사용의 장단점
1. 장점
- 간결하다
- let으로 선언가능하다.
- 반복/조건문 사용시 변수(var)로 선언하여 사이드 이펙트가 발생할 수 있지만 고차함수는 상수 let으로 함수 내부에서 바로 표현해주며 할당할 수 있어 한번에 표현이 가능해서 사이드 이펙트 발생을 막아줍니다.
- 메서드체이닝으로 연결하여 사용할 수 있다.
- 반복/조건문을 한 단어로 표현할 수 있어 코드를 극적으로 단순화 할 수 있다.
2. 단점
- 없는게 단점...그저 완벽
- 너무나 완벽한 고차 함수~~
- 그나마 단점..가독성..
- 탈출 못함...? 요건 좀 안좋은듯...
- hello world
## 오토 레이아웃 공유
---
# (과제) 오토레이아웃 제약 표현해보기
<img width = 400, src = "https://i.imgur.com/Udk1UeM.png">
#### 써니쿠키
- 1. 너비가 같은 두 뷰.
greenView.Top = safeArea.Top + 10
greenView.Bottom = safeArea.Bottom - 10
greenView.leading = safeArea.leading + 10
yellowView.Top = safeArea.Top + 10
yellowView.Bottom = safeArea.Bottom - 10
yellowView.tailing = safeArea.tailing - 10
greenView.width = yellowView.width
greenView.tailing = yellowView.laeding - 10
- 2. 너비가 1:2 인 뷰
greenView.Top = safeArea.Top + 10
greenView.Bottom = safeArea.Bottom - 10
greenView.leading = safeArea.leading + 10
yellowView.Top = safeArea.Top + 10
yellowView.Bottom = safeArea.Bottom - 10
yellowView.tailing = safeArea.tailing - 10
greenView.width = 0.5 X yellowView.width
greenView.tailing = yellowView.laeding - 10
#### 인호
**1:1인 경우**
greenView.Top = safeArea.Top + 10
greenView.Leading = safeArea.Leading + 10
safeArea.Bottom = greenView.Bottom + 10
orangeView.leading = greenView.leading + 10
orangeView.width = 1 * greenView.width
orangeView.Top = greenView.Top
orangeView.Bottom = greenView.Bottom
safeArea.Trailing = orangeView.Trailing + 10
**(그린: 오렌지)1:2인 경우**
greenView.Top = safeArea.Top + 10
greenView.Leading = safeArea.Leading + 10
safeArea.Bottom = greenView.Bottom + 10
orangeView.leading = greenView.leading + 10
orangeView.width = 2 * greenView.width
orangeView.Top = greenView.Top
orangeView.Bottom = greenView.Bottom
safeArea.Trailing = orangeView.Trailing + 10
#### Kyo
greenView.Top = 1 * SafeArea.Top + 10
greenView.Bottom = 1 * SafeArea.Bottom
greenView.Leading = 1 * SafeArea.Leading + 10
greenView.Trailing = 1 * OrangeView.Leading - 10
greenView.Width = 1 * OrangeView
greenView.Height = 1 * SafeArea.Height
OrangeView.Height = 1 * SafeArea.Height
OrangeView.Leading = 1 * greanView.Trailing + 10
OrangeView.Trailing = 1 * SafeArea.Trailing - 10
OrangeView.Top = 1 * SafeArea.Top + 10
OrangeView.Bottom = 1 * SafeArea.Bottom
#### 미니
##### 1:1
greenView.top = safeArea.top + 10
greenView.bottom = safeArea.bottom + 10
greenView.leading = safeArea.leading + 10
greenView.trailing = yellowView.leading - 5
yellowView.top = safeArea.top + 10
yellowView.trailing = safeArea.trailing + 10
yellowView.bottom = safeArea.bottom + 10
yellowView.leading = greenView.trailing - 5
greenView.width = yellowView.width
##### 1:2
greenView.top = safeArea.top + 10
greenView.bottom = safeArea.bottom - 10
greenView.leading = safeArea.leading + 10
greenView.trailing = yellowView.leading - 5
yellowView.top = safeArea.top + 10
yellowView.trailing = safeArea.trailing + 10
yellowView.bottom = safeArea.bottom - 10
yellowView.leading = greenView.trailing - 5
greenView.width = yellowView.width * 0.5