참고로 이번 글은 아래와 같은 코드로 동작하는 것이 목표이다.
for value in MyRange(start: 10, end: 20) { print(value) }이렇게 실행시켰을 때 콘솔에 10에서 20까지 찍혀야 한다.
GeneratorType
시작부터 엉뚱한 이름의 프로토콜이 등장했는데 사실 SequenceType 은 이 GeneratorType 프로토콜을 따르는 인스턴스를 리턴시키는 것이 목표인 프로토콜이라 어쩔 수 없다.GeneratorType은 단순히 .next() 라는 메소드를 구현해야 하는 프로토콜이다. 실제 선언부는 레퍼런스 매뉴얼을 참고하고 여기서는 어떤 식으로 구현하는지를 예로 살펴보자.
struct MyRangeGenerator: GeneratorType { let end: Int var current: Int // 1 typealias Element = Int init(start: Int, end: Int) { self.current = start self.end = end } // 2 mutating func next() -> MyRangeGenerator.Element? { if self.current > end { return nil } let currentValue = self.current self.current++ return currentValue } }위의 코드에서 1, 2로 주석표기한 코드가 핵심이다:
- Element 라는 것을 Int로 재정의 하고 있다. 이는 이 제너레이터가 취급하는 데이터가 Int 타입이라는 의미이다. 만약 다른 타입을 쓰고 싶다면 원하는 타입을 Element로 별칭을 지어주면 된다.
- next() 라는 메소드를 구현하고 있는데 이 녀석이 Element 타입의 데이터를 리턴한다. 이름을 보면 알겠지만 Iteration의 next에 해당하는 녀석이다. 즉 값을 리턴하고 다음 차례의 값을 준비하는 것이 목적인 메소드다. 더이상 돌려줄 값이 없으면 nil을 리턴한다.
SequenceType
이제 핵심에 해당하는 SequenceType이 나왔다. 말 그대로 순차적 나열(Sequence)이 가능한 타입이라는 의미의 프로토콜이다.이 프로토콜은 generate() 라는 메소드를 구현하는 것이 최종 목표인데 앞서 본 GeneratorType을 구현한 인스턴스를 리턴시키는 것임을 유추할 수 있다.
struct MyRange: SequenceType { let start: Int let end: Int // 1 typealias Generator = MyRangeGenerator init(start: Int, end: Int) { self.start = start self.end = end } // 2 func generate() -> MyRange.Generator { return MyRangeGenerator(start: self.start, end: self.end) } }역시 1, 2로 표기한 코드가 핵심이다:
- Generator 라는 별칭은 사용될 제너레이터의 타입을 의미한다. 즉 여기서도 원하는 타입을 Generator 라는 별칭을 붙여주면 된다.
- generate() 메소드의 역활은 실제 제너레이터를 리턴하는 것이다.
for value in MyRange(start: 10, end: 20) { print(value) } // 콘솔에는 10, 11, 12 .... , 20 까지 숫자가 찍힌다.
정리
왜 SequenceType과 GeneratorType이 분리되어 있는지는 아무래도 for-in 구문의 내부 구현에 달린 것 같다. 뭐 그러려니 하고 넘어가는게 좋겠다. 내부 구현까지 들여다 볼 용기는 없으니까.어쨌거나
- 나열가능한 타입은 SequenceType 프로토콜을 따르면 구현이 가능하다.
- 그런데 SequenceType 은 GeneratorType 프로토콜을 따르는 인스턴스를 리턴하는게 목표다.
- GeneratorType은 next() 라는 메소드를 이용하 순차적인 값을 리턴해 주는 것이 목적이다.
그런데 좀 더 생략이 가능하다
마지막에 갑자기 이상한 내용이 나와서 미안하다. 사실 위의 제너레이터로 분리된 타입을 하나로 합쳐서 구현하는 것도 가능하다.위의 MyRangeGenerator를 없애버리고 MyRange 구현을 아래와 같이 바꾸는 것이 가능하다.
struct MyRange: SequenceType { let start: Int let end: Int init(start: Int, end: Int) { self.start = start self.end = end } func generate() -> AnyGenerator<Int> { var current: Int = self.start return anyGenerator { if current > self.end { return nil } let value = current current++ return value } } }SequenceType의 내부에 GeneratorType의 next() 메소드 구현부를 그대로 옮겨놨다. 그 대신 AnyGenerator 라는 특수한 타입의 제너레이터를 리턴하도록 바꾸었다.
결론적으로 구조체(혹은 클래스) 구현은 하나지만 앞서 본 제너레이터 역활을 대신하는 클로져를 anyGenerator 라는 함수를 통해 리턴하고 있다.
어떤 구조가 더 좋을지는 본인이 직접 판단할 문제다. 단지 제너레이터가 복잡하다면 차라리 구현을 분리하는게 더 좋을 것 같다는 생각이다.
[관련글] 스위프트(Swift) 가이드
[관련글] Swift - 구조체(Structure) 훑어보기
[관련글] Swift - 클래스(Class) 훑어보기
[관련글] Swift - 프로토콜(Protocols)
0 comments:
댓글 쓰기