야곰의 스위프트 프로그래밍 - 함수형 프로그래밍과 스위프트

함수형 프로그래밍과 스위프트

클로저

클로저는 참조 타입. Swift에서 클래스와 클로저는 참조 타입. 나머지는 값 타입.

자동 클로저

  • 자동 클로저는 전달 인자를 갖지 않는다.
  • 자동 클로저는 호출되었을 때 자신이 감싸고 있는 코드의 결과값을 반환한다.
  • 자동 클로저는 클로저가 호출되기 전까지 클로저 내부의 코드가 동작하지 않으므로 연산을 지연시킬 수 있다.
  • 자동 클로저 속성을 부여한 매개변수는 클로저 대신에 실행 결과 타입을 전달인자로 받게 되며, 이것이 자동 클로저 매개변수에 전달되면 매개변수가 없는 결과 값의 타입을 반환하는 클로저로 변환해준다.
var strings = ["a", "b", "c"]
func first(_ function: @autoclosure () -> String) {
function()
}
first(strings.removeFirst())
// strigns.removeFirst()의 결과인 a가 전달인자로 들어가며, String 값을 반환하는 매개변수가 없는 클로저로 변환해준다.

옵셔널 체이닝과 빠른종료

빠른종료

  • guard문의 조건에는 Bool 타입이 오므로, 옵셔널 바인딩의 역할을 하지 않을 때도 빠른 종료를 위해 사용할 수 있다.
  • 쉼표를 사용하여 추가 조건을 나열할 수 있으므로, AND 연산과 같은 결과를 낸다.
  • 제어문 전환 명령어를 사용해야 하므로 특정 블록 내부에 위치하지 않는다면 사용이 제한된다.

맵, 필터, 리듀스

Swift는 함수를 일급 객체로 취급. 그러므로 함수를 다른 함수의 전달 인자로 사용할 수 있음.

고차함수 : 매개 변수로 함수를 갖는 함수

자신을 호출할 때 매개변수로 전달된 함수를 실행하여 그 결과를 다시 반환해주는 함수

Sequence, Collection 프로토콜을 준수하는 타입과 옵셔널은 맵을 사용할 수 있음

기존 데이터를 변형하는 데 많이 사용함

for-in 구문과 비교하여 코드 재사용 측면과 컴파일러 최적화 측면에서 유리하며, 다중 스레드 환경에서 사이드 이펙트를 방지할 수 있다.

필터

컨테이너 내부의 값을 걸러서 추출하는 역할을 하는 고차함수

특정 조건에 맞게 걸러내는 역할을 할 수 있음. 전달되는 함수 객체의 반환값이 Bool이므로 필터링될 조건에 대해 서술함

리듀스

컨테이너 내부의 컨텐츠를 하나로 합하는 기능을 실행하는 고차함수

적절하게 활용하면 코드의 양을 줄이면서, 보기 좋은 코드를 작성할 수 있음.

모나드

특정한 상태로 값을 포장. Swift에서는 옵셔널이 모나드를 구현한 형태. 값이 있을지 없을지 모르는 상태 속에 포장.

컨텍스트

컨텐츠를 담은 어떤 것

// Swift의 옵셔널 타입 정의
public enum Optional<Wrapped>: ExpressibleByNilLiteral {
case none
case some(Wrapped)
}

옵셔널에 값이 있다면 열거형의 .some 케이스의 값을 갖게 됨, 값이 없다면 열거형의 .none 케이스 값을 갖게 됨

옵셔널을 추출한다는 것은 .some 케이스의 연관 값을 꺼내오는 것

옵셔널은 somenone 두 가지의 컨텍스트를 가짐

  • 컨텍스트는 2라는 값을 가지고 있다 : some 컨텍스트
  • 컨텍스트는 존재하지만 내부에 값이 없다 : none 컨텍스트

함수객체

map 함수를 적용할 수 있는 컨테이너 타입. Array, Dictionary, Set 등등 많은 컬렉션 타입은 함수객체임.

extension Optional {
func map<U>(f: (Wrapped) -> U) -> U? {
switch self {
case .some(let x): return f(x)
case .none: return .none
}
}
}

옵셔널에 map 함수를 호출하면, some 컨텍스트인지 none 컨텍스트인지 확인함

  • some 컨텍스트이면 컨텍스트에 들어있는 컨텐츠를 가지고 인자로 전달된 함수를 수행한 후 컨텍스트에 다시 포장하여 반환
  • none 컨텍스트이면 none 컨텍스트를 반환

모나드

값이 있을 수도 있고 없을 수도 있는 컨텍스트를 갖는 함수객체 타입

옵셔널(모나드)에서의 flatMap

  • func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?

    • Optional 인스턴스가 nil이 아닐 때 주어진 클로저를 평가하는데, 매개변수로 래핑되지 않은 값을 넘긴다.

    • 내부의 값을 알아서 더 추출해주어 내부에 포장된 값도 추출해낼 수 있다

    • let a: Int? = 3
      let b = a.map { Optional($0) }
      let c = a.flatMap { Optional($0) }
      print(b)    // Optional(Optional(3))
      print(c)    // Optional(3)
    • map에서 U == Int? 이므로 결과의 타입은 Int??

    • flatMap에서 U? == Int? 이므로 결과의 타입은 Int?

시퀀스에서의 flatMap

  • func flatMap<SegmentOfResult>(_ transform: (Self.Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence
    • 시퀀스의 각 요소에 주어진 변형을 호출하여 이어진 결과를 포함하는 배열을 반환
    • [[1, 2], [3, 4]]에 호출하여 [1, 2, 3, 4] 와 같이 만들 수 있는 메소드. 내부의 값을 1차원적으로 펼쳐놓음
  • func flatMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
    • 시퀀스의 각 요소에 주어진 변형을 호출하여 nil이 아닌 결과를 포함하는 배열을 반환
    • [“1”, “2”, nil, “5”]에 호출하여 [1, 2, 5]와 같이 만들 수 있는 메소드
    • Swift 4.1에서 compactMap으로 이름이 바뀜

옵셔널 체이닝, 옵셔널 바인딩, 플랫맵 등은 모나드와 관련된 연산