-
Swift - Sequence와 Collection (Sequence와 Collection의 차이)Programming/Swift 2021. 8. 11. 14:45
안녕하세요 :)
오늘은 Array, Dictionary, Set을 공부하기 전에 Sequence와 Collection Protocol에 대해서 공부해보도록 하겠습니다.
이번장은 이해하기 어려운 부분이 많이 있어서 이해하기 힘드시면 나중에 보시는 것을 추천드립니다.
Sequence와 Collection은 프로토콜입니다. (프로토콜에 대해서는 이후에 자세히 다루겠습니다.)
그리고 Sequence 기반으로 Collection이 만들어져있습니다.
또한 Sequence 프로토콜에는 유용한 메서드들이 많이 선언되어 있습니다.
contains, map, filter, first, drop, sorted, compare 등등 우리가 String에서도 유용하게 사용했던 메서드들이 많습니다.
String에서 이러한 메서드를 사용할 수 있었던 이유는 String이 Character의 collection이기 때문이고 Collection은 Sequence를 기반으로 구현되어 있기 때문입니다.
Sequence
Sequence는 element에 순차적이고 반복적인 접근이 가능한 type입니다.
Sequence는 값의 리스트이고 이 값 하나하나를 element라고 합니다.
Sequence의 element를 반복하는 가장 일반적인 방법은 for-in loop를 사용하는 것입니다.
이전에 공부했던 for-in loop에서 range에 해당하는 부분에 Sequence 프로토콜을 채용한 type만 올 수 있습니다.
Sequence의 특징은 다음과 같습니다.
1. 무한하거나 유한하다.
2. 여러 번의 반복을 보장하지 않는다.
첫 번째 무한하거나 유한하다는 말은 1 ... 10 처럼 element의 개수가 유한하거나 1... 처럼 element의 갯수가 무한한 것을 의미합니다.
Sequence는 이 두 가지 모두를 포함합니다.
두 번째 여러 번의 반복을 보장하지 않는다는 의미는 예를 들어 for - in문을 반복해서 사용하는 경우 첫 번째 반복은 모든 element의 반복을 보장하지만 이후 반복된 for - in문에 대해서는 처음부터 반복될지 중단된 중간부터 반복될지에 대해서 보장하지 않습니다.
뭔 말인지 이해하기 조금 어려우니 예시를 보겠습니다.
위 코드는 애플 문서에 나와있는 예시입니다.
첫 번째 for-in문에서 break로 반복이 중간에 중단되었습니다.
일반적으로 우리가 생각하는 코드의 흐름은 break후 2번째 for-in문에서 다시 sequence의 첫 번째 element부터 반복된다고 생각합니다.(이런 경우는 Collection인 경우입니다.)
하지만 Sequence는 이런 경우 line 5의 for - in문의 반복이 sequence의 첫 번째 element부터 다시 시작할지 break 된 부분부터 시작할지 보장하지 못한다는 의미입니다.
Custom Sequence 만들기
Sequence를 잘 이해하기 위해서 직접 sequence를 만들어보도록 하겠습니다.
그럼 Sequence 프로토콜을 자세히 살펴보겠습니다.
associatedtype으로 IteratorProtocol을 채용하고 있는 Iterator가 내부적으로 선언되어 있습니다.
Sequence와 IteratorProtocol은 아주 밀접한 관계가 있습니다.
Custom Sequence를 구현하려면 IteratorProtocol을 알아야 합니다.
IteratorProtocol은 Sequence의 element를 하나씩 제공해주는 타입입니다.
Sequence는 iterator를 생성해서 element에 접근할 수 있도록 구현되어있습니다.
Iterator는 반복 프로세스를 추적하고 sequence의 element를 하나씩 리턴해주는 역할을 합니다.
Swift는 내부적으로 sequence와 collection의 iterator를 이용해서 for-in 문을 구현했습니다.
위 코드는 우리가 일반적으로 사용하는 for-in문입니다.
배열을 만들어서 for-in문으로 반복시키면서 element를 출력하도록 했습니다.
이 코드는 line 6과 같이 내부적으로 Sequence에 선언되어 있는 makeIterator() 메서드를 호출하여 iterator를 생성하고 IteratorProtocol에 선언되어 있는 next() 메서드를 호출하여 이 메서드가 nil을 리턴할 때까지 element를 num에 바인딩시키면서 콘솔에 출력하도록 구현되어 있습니다.
그럼 이제 custom sequence를 만들어 보겠습니다.
Sequence 프로토콜을 채용한 Type을 만들려면 protocol의 required를 만족시켜야 하죠??
그렇다면 makeIterator() 메서드와 Iterator, Element를 정의해야 하는데 Iterator는 IteratorProtocol을 채용해야 합니다.
한마디로 Sequence 프로토콜을 채용하려면 IteratorProtocol로 채용해야 합니다.
그럼 IteratorProtocol의 required를 보도록 하겠습니다.
next() 메서드만 선언해준다면 IteratorProtocol을 채용할 수 있습니다.
next() 메서드는 관련된 시퀀스를 한 단계씩 진행하고 현재 element를 리턴해주면 됩니다.
이때 리턴하는 element가 자연스럽게 required인 associatedtype Element가 되게 됩니다.
그리고 시퀀스가 끝났다면 nil을 리턴해주도록 선언해주면 됩니다.
그럼 custom sequence를 만들기 위해서는 Sequece 프로토콜을 채용하고 makeIterator() 메서드를 선언하고,
IteratorProtocol을 채용한 Iterator와 next() 메서드를 선언해주면 됩니다.
말로 하니 이해가 잘 안 되죠??
예시를 통해서 이해해보도록 하겠습니다.
예시로 들 시퀀스는 Countdown입니다.
속성으로 정수를 전달하면 1씩 감소하면서 0이 되면 멈추는 custom sequence를 만들어 보겠습니다.
Sequence 프로토콜을 채용한 Countdown 구조체를 선언했습니다.
첫 카운트 시작 숫자인 startNumber를 속성으로 선언하고 makeIterator() 메서드를 구현했습니다.
makeIterator() 메서드는 CountdownIterator를 리턴하도록 했습니다.
그럼 이제 CountdownIterator를 선언해야겠죠??
IteratorProtocol 프로토콜을 채용한 Iterator인 CountdownIterator 구조체를 선언합니다.
저는 따로 밖으로 빼냈지만 NestedType으로 Countdown 구조체 안에서 선언해도 됩니다.
IteratorProtocol 프로토콜을 채용하려면 next() 메서드만 구현하면 된다고 했죠??
countdown은 count에서 1씩 감소하여서 0이 되면 nil을 리턴해주면 됩니다.
이 방법이 정석적으로 custom sequence를 만드는 방법입니다.
그런데 만약 Sequence 자체가 Iterator의 역할을 할 수 있다면 이 코드는 좀 더 간단하게 구현이 가능합니다.
이전 코드를 보시면 startNumber와 count는 다를 게 없습니다.
그래서 makeIterator() 메서드를 보면 CountdownIterator의 인스턴스를 생성할 때 count에 startNumber를 넣고 있습니다.
Countdown 자체가 Iterator가 돼도 아무 문제가 없는 코드입니다.
Countdown가 Seqence와 IteratorProtocol을 모두 채용하도록 했습니다.
이렇게 자기 자신이 Iterator가 된다면 makeIterator() 메서드도 직접 선언하지 않아도 됩니다.
next() 메서드만 선언해준다면 이전과 똑같은 기능을 하는 custom Sequence를 만들 수 있습니다.
Collection
Collection은 element를 여러 번 반복이 가능하고 인덱스로 접근이 가능한 sequence 입니다.
Collection은 Sequence를 기반으로 만들어져 있다고 했었죠??
Collection 프로토콜을 채용하면 Sequence에서 사용하던 유용한 메서드, 속성을 모두 사용할 수 있습니다.
Collection의 특징은 다음과 같습니다
1. Collection은 유한하다.
2. Index로 element에 접근이 가능하다.
3. 여러 번의 반복을 보장한다. (원하는 만큼 반복할 수 있다.)
Collection은 Sequence에서 불편한 부분을 보완했다고 생각하면 조금 이해하기 쉽습니다.
첫 번째 특징 Collection은 element의 개수를 알 수 있습니다. 유한하기 때문이죠.
그렇기 때문에 두 번째 특징인 Index로 접근이 가능합니다.
유한하기 때문에 startIndex를 알 수 있고 endIndex도 알 수 있으므로 각 element를 Index로 바로 접근할 수 있습니다.
첫 번째 element는 startIndex로 마지막 element는 endIndex의 이전 Index로 접근하는 게 가능한 것을 볼 수 있습니다.
(endIndex는 마지막 element를 의미하지 않습니다. 이전에 StringIndex에서 알아보았습니다.)
Index로 element에 접근이 가능한 것을 볼 수 있습니다.
Seqence는 index로 접근할 수 없는 것도 한번 확인해 보겠습니다.
Sequence는 Index를 제공하지 않기 때문에 에러가 나는 것을 확인해 볼 수 있습니다.
Index를 이용하여 반복적으로 랜덤 액세스가 가능하기 때문에 세 번째 특징인 여러 번 iterate가 가능합니다.
Sequece는 여러 번의 iterate를 보장하지 않았지만 Collection은 이를 완벽하게 보장해 줍니다.
그럼 Collection이 어떻게 구현되어 있는지 간단하게만 보겠습니다.
Seqence에는 없던 Index가 associatedtype으로 선언되어 있습니다.
그리고 startIndex, endIndex가 있습니다.
그리고 Index로 element에 접근하기 위한 subscript도 선언되어있는 것을 확인할 수 있습니다.
마지막으로 다음 index를 얻기 위한 index(after:) 메서드도 있습니다.
만약 custom collection을 만들고 싶다면 위에 설명한 5가지 속성과 메서드만 정의해주면 됩니다.
그럼 한 가지 생각이 드시죠??
'Collection은 Sequence를 기반으로 만들었는데 그럼 Sequence 프로토콜의 required인 next() 메서드는 정의하지 않아도 되는 건가???'라는 생각이요!
먼저 답을 말씀드리자면 next()를 정의하지 않아도 됩니다.
- startIndex로 첫 번째 element를 얻고
- index(after:) 메서드로 다음 index를 얻을 수 있으며
- subscript인 self[Index]로 이후 element를 얻을 수 있고
- endIndex와 2번으로 얻은 index가 같다면 iterate를 종료한다.
이러한 동작으로 next()와 같은 동작을 구현할 수 있기 때문입니다.
이렇기 때문에 위 5가지 속성과 메서드만 정의한다면 Sequence가 제공하는 속성, 메서드부터 Collection이 제공하는 속성, 메서드를 모두 사용할 수 있습니다.
Custom collection 예시는 나중에 기회가 되면 다뤄보도록 하겠습니다.
정리
마지막으로 정리를 해보고 마무리하겠습니다.
- Sequence는 범위가 무한하거나 유한하며 여러 번의 iterate를 보장하지 않는다.
- Collection은 범위가 유한하며 Index로 element에 접근이 가능하고 여러번의 iterate를 보장한다.
Swift를 공부하다가 Sequnce와 Collection은 무슨 차이 일까??라는 궁금증으로 시작된 포스팅이었는데 처음 생각한 것보다 포스팅이 매우 길어졌네요.
아직 다루지 못한 BidirectionalCollection, MutableCollection, RangereplaceableCollection 등이 있는데 나중에 기회가 되면 다루도록 하겠습니다.
참고자료
https://developer.apple.com/documentation/swift/sequence
https://academy.realm.io/kr/posts/try-swift-soroush-khanlou-sequence-collection/
https://soooprmx.com/swift-array-02-sequence-프로토콜/
728x90'Programming > Swift' 카테고리의 다른 글
Swift - Array (0) 2021.08.30 Swift - Foundation Collection & Swift Collection (0) 2021.08.28 Swift - CharacterSet (1) 2021.08.07 Swift - String CompareOptions (2) (0) 2021.08.05 Swift - String CompareOptions (1) (0) 2021.08.04