Let's say I have this code:
func work<S: Sequence>(sequence: S) {
// do stuff
}
How could I figure out how many elements there are in sequence
?
The obvious version I'd go for is pretty inefficient:
var count = 0
for element in sequence {
count += 1
}
There must be a nicer way, right?
To add Sequence conformance to your own custom type, add a makeIterator() method that returns an iterator. Alternatively, if your type can act as its own iterator, implementing the requirements of the IteratorProtocol protocol and declaring conformance to both Sequence and IteratorProtocol are sufficient.
A sequence protocol requires a factory method that returns an Iterator type as shown in the code below: An iterator is a type that conforms to Iterator protocol. The protocol has a mutating method next() that returns individual values one after the other until it can't anymore, in which case it returns a nil.
I do not think that there is a better method for an arbitrary type conforming to
SequenceType
. The only thing that is known about a sequence is that
is has a generate()
method returning a GeneratorType
, which in turn
has a next()
method. The next()
method advances to the next
element of the sequence and returns it, or returns nil
if there
is no next element.
Note that it is not required at all that next()
eventually returns
nil
: a sequence may have "infinite" elements.
Therefore enumerating the sequence is the only method to count its elements. But this need not terminate. Therefore the answer could also be: A function taking a sequence argument should not need to know the total number of elements.
For types conforming to CollectionType
you can use the
countElements()
function (renamed to count()
in Swift 1.2).
There is also underestimateCount()
:
/// Return an underestimate of the number of elements in the given
/// sequence, without consuming the sequence. For Sequences that are
/// actually Collections, this will return countElements(x)
func underestimateCount<T : SequenceType>(x: T) -> Int
but that does not necessarily return the exact number of elements.
A more "functional" approach would be:
let count = sequence.reduce(0) { acc, row in acc + 1 }
Or even make an extension:
extension Sequence {
var count: Int { return reduce(0) { acc, row in acc + 1 } }
}
let count = sequence.count
Note This does still suffer from the basic sequence problem that you have to consume the sequence to count it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With