flatMap 및 이중 Optional

6301 단어
지난번에 웨이보를 할 때 @당교가 웨이보를 크게 올린 몇 단락의 코드를 보았는데 다음과 같은 몇 가지 상황으로 정리했습니다.
let arr: [Int?] = [1,3,nil,4]

let arr1: [Int?] = arr.flatMap { $0 } // [{some 1}, {some 3}, nil, {some 4}]

let arr2: [Int?] = arr.flatMap {
  next -> Int?? in
  next
} // [{some 1}, {some 3}, nil, {some 4}]

let arr3: [Int?] = arr.flatMap { 
    next -> Int? in
    next
} // [{some 1}, {some 3}, {some 4}]

let arr4 = arr.flatMap { $0 } // [1,3,4]

let arr5: [Int] = arr.flatMap { $0 } // [1,3,4]

나는 왜 이 몇 단락의 코드가 flat Map에 다른 행동을 하게 하는지 이해하지 못하고 며칠 동안 끊임없이 생각했지만 아직 결과가 없어서 Stack Over Flow에 가서 이 문제를 제기하기로 결정했다. 문제는 여기에 있다. 다행히도 바로 누군가가 이 문제에 대답했고 대답도 잘했다. 나는 다른 사람의 답안과 자신의 이해를 기록했다.
많은 사람들이 Array의 flatMap command+click에 대해 flatMap의 성명을 들여다볼 때 Sequence Type 아래에 두 개가 있다는 것을 발견할 수 있을 거라고 믿습니다.
public func flatMap(transform: (Self.Generator.Element) throws -> S) rethrows -> [S.Generator.Element]

public func flatMap(@noescape transform: (Self.Generator.Element) throws -> T?) rethrows -> [T]

첫 번째는 그룹을 평평하게 찍는 데 쓰이고 두 번째는nil값을 제거하는 데 쓰인다. 여기는 이 문제에 대한 부분적인 해석이 있지만 아직 상세하지 않다. 그래서 나는 Swift의 원본 코드에서 찾았고 마침내 flatMap의 실현을 찾았다. 여기에 부분적인 코드를 붙였다.
public func flatMap(
    _ transform: (Elements.Iterator.Element) -> SegmentOfResult
  ) -> LazySequence<
    FlattenSequence>> {
    return self.map(transform).flatten()
 }
  
public func flatMap(
    _ transform: (Elements.Iterator.Element) -> ElementOfResult?
  ) -> LazyMapSequence<
    LazyFilterSequence<
      LazyMapSequence>,
    ElementOfResult
  > {
    return self.map(transform).filter { $0 != nil }.map { $0! }
}

첫 번째 flatMap은 먼저 자신에게 맵 조작을 한 다음에 자신에게 flatten 조작을 한다.
두 번째 flatMap은 비교적 재미있다. 먼저 자신에게 맵 조작을 한 다음에 nil 값을 필터하고 마지막에 optional을 일반 값으로 전환한다.
분명히 우리가 여기서 토론하고자 하는 것은 두 번째 플래트맵이다.
flatMap의 반환값에 대해 어떠한 제한도 하지 않을 때 위의 코드에 있는arr4에 대응합니다. 이때 T는 인트로 추정되기 때문에tansform 클립을 간소화한 형식은 Int? -> Int?입니다. 이 클립은 맵에 전달되는 것이므로 주의하십시오.
Array 맵의 설명을 보십시오.
public func map(@noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]

맵의 원리를 생각해 보면 매우 간단하다. 사실은 전체 수조를 두루 훑어보고 모든 원소에transform 함수를 실행한 다음에 이 원소를 포함하는 새로운 수조를 되돌려주는 것이다.우리가 전송한transform 클립에 대해 이 맵은 아무것도 하지 않은 것과 같기 때문에 다음 Filter 작업에 사용할 수 있는 그룹입니까? 아니면 [Int?]입니까?다음은 순서대로 나일값을 제거하고 비optional값으로 비추는 것입니다.
arr5에 대해서 말하자면, 상황은 같다.
flatMap의 반환 값이 [Int?]임을 나타낼 때상황이 달라졌다.T가 Int?으로 추정됩니다.트랜스포머 클립을 간소화한 후의 유형은 Int? -> Int??로,arr1과arr2는 분명히 등가이다. 우리가 먼저 분석을 해 보자. 먼저 그룹을 옮겨다니며 그룹의 요소에 대해transform 조작을 한다. 트랜스포머 클립이 되돌아오는 유형은 Int???그래서 이 때 되돌아오는 새 그룹의 원소의 유형은 Int??이거나 더 정확히 Optional>이다. 다음에 그룹에 nil값을 제거한다. 왜냐하면 이 그룹에 nil이 없기 때문이다(이전의 nil은 맵에 의해 Optional). 그래서 Filter는 작용하지 않았고 마지막으로 맵은 원소를 강제로 해제한다Optional.
여기까지 일이 끝나지 않았습니다. 제가 가장 이해할 수 없는 것은arr3이라는 상황입니다. arr1과 마찬가지로 반환값은 모두 강제로 [Int?]로 가리키고 있습니다.그러나 변화가 발생한 것은 전송된transform 클립이다. 원래 받아야 할 유형은 Int? -> Int??인데 전송된transform 클립은 Int? -> Int?인데 왜 가능한가?
내가 앞서 언급한 Stack Over Flow의 대답에서 이 열성적인 @originaluser2는 나에게 이런 대답을 주었다.
Because you explicitly annotate the return type of the closure to be Int? , the closure will get implicitly promoted from (Element) -> Int? to (Element) -> Int?? (closure return types can get freely promoted in the same way as other types) – rather than the element itself being promoted from Int? to Int?? , as without the type annotation the closure would be inferred to be (Element) -> Int??
번역하면 Int? -> Int?Int? -> Int??로 승급할 수 있거나 하나의 수용Int? -> Int??의 함수에 대해서도 수용할 수 있다Int? -> Int?.
그리고 그는 아래의 예를 들었다.
func optionalIntArrayWithElement(closure: () -> Int??) -> [Int?] {
    let c = closure() // of type Int??
    if let c = c { // of type Int?
        return [c]
    } else {
        return []
    }
}

// another quirk: if you don't explicitly define the type of the optional (i.e write 'nil'),
// then nil won't get double wrapped in either circumstance
let elementA : () -> Int? = {Optional.None} // () -> Int?
let elementB : () -> Int?? = {Optional.None} // () -> Int??

// (1) nil gets picked up by the if let, as the closure gets implicitly upcast from () -> Int? to () -> Int??
let arr = optionalIntArrayWithElement(elementA)

// (2) nil doesn't get picked up by the if let as the element itself gets promoted to a double wrapped optional
let arr2 = optionalIntArrayWithElement(elementB)

if arr.isEmpty {
    print("nil was filtered out of arr") // this prints
}

if arr2.isEmpty {
    print("nil was filtered out of arr2") // this doesn't print
}

우리는 optionalIntArrayWithElement에 필요한 것은 하나() -> Int??의 클로즈업이고 elementA는 하나() -> Int?이지만 전달에 전혀 문제가 없다는 것을 알 수 있다.
그런 다음 코드에 대한 설명을 봅니다.
another quirk: if you don't explicitly define the type of the optional (i.e write 'nil'), then nil won't get double wrapped in either circumstance
즉, 단순히 닐에게 부여된 변수Int??만 있다면 이 닐은 이중optionla에 감싸이지 않는다. 이중optional에 대해서는 야옹신 편을 볼 수 있다.
이때 가장 어려운 부분이 왔습니다. 다음은 제 이해입니다. 완전히 정확한지 여러분께 토론할 수 있도록 하겠습니다.
처음부터 말하자면, 우선 우리는 arr를 [Int?]라고 성명한다.안의nil값에 대해 이때와optional.None 등가, 우리map arr일 때, 즉 안의원소를transform 조작할 때 이때의transform 함수는 되돌아오기Int??로 높아졌지만 원소의nil값은 보통nil이기 때문에 이때nil는Optional>.None로 변하지 않았고 일반적인 비nil은 당연히 Optional>.Some(3) 등으로 변화할 수 있다.그래서 마지막에nil값은 제거되었지만 다른 비nil값은 여전히 변하지 않았다.
그러나 여기에 또 다른 문제가 있다. 바로transform이 기본적으로 Int?? 형식으로 되돌아갈 때, nil 값이 이중 nil로 비추는 것이 분명하다. 왜 그런가?묵인하는 행동인가?누군가 해답을 분석할 수 있기를 기대한다.

좋은 웹페이지 즐겨찾기