티스토리 뷰

Swift

Swift ) Sequence

Zedd0202 2022. 2. 5. 15:24
반응형

 

안녕하세요 :) Zedd입니다.

오늘은 Sequence에 대해서 공부!

 

# Sequence

Sequence가 익숙하지 않으시다면, 혹시 Collection은 들어보셨나요?

Collection은 프로토콜이며 

Swift에서 가장 유명한 Collection Type들인 Array, Set, Dictionary는 Collection프로토콜을 conform하고 있습니다.

그리고 이 Collection프로토콜은 Sequence를 conform하고 있습니다. 

네! 맞아요! Sequence도 프로토콜입니다. 

protocol Sequence

Sequence는 ✔️ 한번에 하나씩 단계(step)별로 진행할 수 있는 값 목록(list of values) ✔️ 입니다. 

이 Sequence의 요소를 반복하는 가장 일반적인 방법은 for-in loop를 사용하는 것이죠. 

let oneTwoThree = 1...3
for number in oneTwoThree {
    print(number)
}
// Prints "1"
// Prints "2"
// Prints "3"

바로 이런식으로요!

 

# Conforming to the Sequence Protocol

제가 만든 custom type인 Zedd로 

struct Zedd {}

let zedd = Zedd()
for value in zedd {} 🚨 // error! for-in loop requires 'Zedd' to conform to 'Sequence'

for-in loop를 사용할 수 없는 이유는, Zedd가 Sequence를 conform하고 있지 않기 때문입니다.

그렇다면 일단 Zedd가 Sequence를 conform하도록 해야겠네요.

struct Zedd: Sequence {}

Sequence가 요구하는 required method는 

public protocol Sequence {
   ...
   func makeIterator() -> Self.Iterator
}

makeIterator()인데요. 

Interator를 만드려면 IteratorProtocol을 conform하는 타입을 만들어야 합니다.

struct ZeddIterator: IteratorProtocol {
    typealias Element = Int
    var current = 1
    
    mutating func next() -> Element? {
        defer { current += 1 }
        return current
    }
}

struct Zedd: Sequence {
    
    func makeIterator() -> some IteratorProtocol {
        return ZeddIterator()
    }
}

이런식으로요!

하지만, 타입이 Sequence, IteratorProtocol 모두 conform한다면 타입 자체가 Iterator로 행동할 수 있게 되기 때문에 makeIterator()를 구현해주지 않아도 됩니다.

즉, 

struct Zedd: Sequence, IteratorProtocol {
    
    typealias Element = Int
    var current = 1
    
    mutating func next() -> Element? {
        defer { current += 1 }
        return current
    }
}

이렇게만 해주면 끝!

let zedd = Zedd()
for value in zedd { ✅
    print(value)
}

자..이제 위 코드는 컴파일에러를 일으키지 않습니다. 🎉

 

다만 value가 언제까지 찍힐까요? 

struct Zedd: Sequence, IteratorProtocol {
    
    var current = 1
    
    mutating func next() -> Int? {
        defer { current += 1 }
        return current
    }
}

그냥 무작정 +1을 더하고있기 때문에....

for loop는 끝나지 않습니다 ㅎ

요런 상황을 방지하려면,

// 방법 1
struct Zedd: Sequence, IteratorProtocol {
    
    var current = 1
    
    mutating func next() -> Int? {
        if current > 10 { return nil }
        defer { current += 1 }
        return current
    }
}

// 방법 2
for value in zedd {
    if value > 10 { break }
    print(value)
}

// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
// 10

Zedd의 next쪽에다가 제한을 걸던가, for-in loop에 걸던가 하면 됩니다! 

💡 Iterator에서 nil을 리턴하는것은 완료를 의미합니다.

 

# 컴파일러가 for-in loop를 만났을 때

자..이제 우리는 iterator도 알고.. 좀 아는 것 같습니다?

그럼 컴파일러가 for-in loop를 만났을 때 내부적으로 어떤 일들을 하는지 알아보겠습니다.

let arr = [1, 2, 3]
for value in arr { }

아주 평범한 for-in loop입니다.

 

컴파일러는 이때 몇가지 간단한 변환을 하게 되는데요. 

var iterator = arr.makeIterator()
while let value = iterator.next() {

}

iterator를 생성하고 while루프로 변환시킨다고 합니다.

요 내용은 WWDC21 ) Meet AsyncSequence에서 본 것을 참고하여 적어봤어요 ㅎㅎ 

 


 

사실 Sequence를 conform하는 타입을 만들거나 한 일이 거의 없어서 ㅎㅎ 몰랐던 부분도 알게되어서 좋네요.

틀린점이 있다면 댓글로 남겨주세요! 👻

 

 

반응형

'Swift' 카테고리의 다른 글

[Swift 5.7] if let shorthand  (1) 2022.07.09
Swift 5.6 ) Introduces existential any  (9) 2022.04.03
Literal  (0) 2022.01.23
Swift ) prefix / suffix  (1) 2021.12.01
Swift 5.5 ) Codable synthesis for enums with associated values  (0) 2021.11.18