当前位置: 动力学知识库 > 问答 > 编程问答 >

swift - Strange SequenceType behaviour when using a wrapper

问题描述:

I have a wrapper that wraps a sequence:

struct SequenceWrapper<T>: SequenceType {

var sequence: AnySequence<T>

func generate() -> AnyGenerator<T> {

return sequence.generate()

}

}

let wrapper = SequenceWrapper(sequence: AnySequence(1 ... 10))

If I make two generators and call next() on both, nothing strange happens:

let generator = wrapper.generate()

let another = wrapper.generate()

generator.next() // 1

another.next() // 1

So far, so good. However, if I first call dropFirst() on my wrapper, I get strange behaviour:

let wrapper = SequenceWrapper(sequence: AnySequence(1 ... 10))

let trimmed = wrapper.dropFirst()

let generator = trimmed.generate()

let another = trimmed.generate()

generator.next() // 2

another.next() // 3, huh?

If I use dropLast() instead of dropFirst(), the outputs are 1 and 1, as expected.

If I don't use the wrapper struct I made, but a direct AnySequence instance instead, nothing out of the ordinary happens:

let sequence = AnySequence(1 ... 10)

let trimmed = sequence.dropFirst()

let generator = trimmed.generate()

let another = trimmed.generate()

generator.next() // 2

another.next() // 2, as expected

I can't make any sense of this. What's going on here?

网友答案:

You would have to debug through the behavior to understand the specific behavior, but this most likely happens because SequenceType is not required to be iterable multiple times.

SequenceType makes no requirement on conforming types regarding whether they will be destructively "consumed" by iteration. To ensure non-destructive iteration, constrain your sequence to CollectionType.

As a consequence, it is not possible to run multiple for loops on a sequence to "resume" iteration:

for element in sequence {
  if ... some condition { break }
}

for element in sequence {
  // Not guaranteed to continue from the next element.
  // [mine: not guaranteed to restart from the beginning either]
}

SequenceType makes no requirement about the behavior in that case. It is not correct to assume that a sequence will either be "consumable" and will resume iteration, or that a sequence is a collection and will restart iteration from the first element. A conforming sequence that is not a collection is allowed to produce an arbitrary sequence of elements from the second generator.

In other words, the two generators that you get from calling generate() twice are not guaranteed to not interfere with one another. In fact, it's legal for a type to implement both SequenceType and GeneratorType and implement generate() as return self. Any shared state (from member references, or if the sequence itself is a reference type) will be shared across generators, which is why you should only make one.

网友答案:

The internal dropFirst() method returns a new instance of AnySequence (as you can see in this line: https://github.com/apple/swift/blob/master/stdlib/public/core/Sequence.swift#l1070).

What you need to add to your SequenceWrapper are these lines:

func dropFirst() -> SequenceWrapper.SubSequence {
    return sequence.dropFirst()
}

Here is a short Playground snippet for testing:

struct SequenceWrapper<T>: SequenceType {
    var sequence: AnySequence<T>

    func generate() -> AnyGenerator<T> {
        return sequence.generate()
    }

    func dropFirst() -> SequenceWrapper.SubSequence {
        return sequence.dropFirst()
    }
}

let wrapper = SequenceWrapper(sequence: AnySequence(1 ... 10))
var generator = wrapper.generate()
var another = wrapper.generate()

generator.next() // 1
another.next()   // 1

var trimmed = wrapper.dropFirst()

generator = trimmed.generate()
another = trimmed.generate()

generator.next() // 2
another.next() // 2! :)
分享给朋友:
您可能感兴趣的文章:
随机阅读: