如何在Swift中创建生成器?

时间:2017-05-30 03:28:07

标签: swift generator

我可以在Swift中创建一个生成器吗?

使用迭代器,我需要存储中间结果,例如:

static

在此示例中,我需要存储struct Countdown: IteratorProtocol, Sequence { private var value = 0 init(start: Int) { self.value = start } mutating func next() -> Int? { let nextNumber = value - 1 if nextNumber < 0 { return nil } value -= 1 return nextNumber } } for i in Countdown(start: 3) { print(i) } // print 1 2 3

在我的情况下,我想使用generator而不是迭代器,因为我不想在每个value中存储序列的中间结果。

4 个答案:

答案 0 :(得分:1)

了解生成器如何工作(以及它们在swift中不那么重要的原因)起初很难来自Python。

直到Swift v2.1,有一个名为GeneratorType的协议。这在Swift v3.0 +中被重命名为IteratorProtocol。您可以遵循此协议,使您自己的对象执行即时计算,类似于Python中可以执行的操作。

可以在Apple文档中找到更多信息:IteratorProtocol

IteratorProtocol页面的一个简单示例:

struct CountdownIterator: IteratorProtocol {
    let countdown: Countdown
    var times = 0

    init(_ countdown: Countdown) {
        self.countdown = countdown
    }

    mutating func next() -> Int? {
        let nextNumber = countdown.start - times
        guard nextNumber > 0
            else { return nil }

        times += 1
        return nextNumber
    }
}

let threeTwoOne = Countdown(start: 3)
for count in threeTwoOne {
    print("\(count)...")
}
// Prints "3..."
// Prints "2..."
// Prints "1..."

但是,您需要考虑使用生成器的原因:

Swift自动执行某些操作&#34;在写入时称为副本。&#34;这意味着许多使用Python生成器来避免对象集合(数组,列表,字典等)的大量复制成本的情况在Swift中是不必要的。您可以使用其中一种使用写入时复制的类型免费获得。

Which value types in Swift supports copy-on-write?

也可以使用包装器强制几乎任何对象在写入时进行复制,即使它不是集合的一部分:

How can I make a container with copy-on-write semantics?

swift中的优化通常意味着您不必编写生成器。如果您确实需要(通常是因为数据繁重,科学计算),则可能如上所述。

答案 1 :(得分:0)

根据您提供的代码和我所拥有的生成器的一点知识,您可以执行类似

的操作
struct Countdown {
    private var _start = 0
    private var _value = 0

    init(value: Int) {
        _value = value
    }

    mutating func getNext() -> Int? {
        let current = _start
        _start += 1
        if current <= _value {
            return current
        } else {
            return nil
        }
    }
}

然后无论你想在哪里使用它,你都可以做类似

的事情
var counter = Countdown(value: 5)
while let value = counter.getNext() {
    print(value)
}

答案 2 :(得分:0)

我有一个类似于上面的解决方案,但对它有一点点“产量”感觉。

struct Countdown
{
    static func generator(withStart: Int) -> () -> Int?
    {
        var start = withStart + 1
        return {
            start = start - 1
            return start > 0 ? start : nil
        }
    }
}

let countdown = Countdown.generator(withStart: 5)

while let i = countdown()
{
    print ("\(i)")
}

答案 3 :(得分:0)

Walter提供了很多很好的信息,通常你不应该在Swift中这样做,但即使你想要一个迭代器,正确的方法是使用合成,而不是建立自己的。 Swift有很多现有的序列可以组合起来创建你想要的东西而不保持你自己的状态。因此,在您的示例中,您与范围的迭代器不同:

struct Countdown: Sequence {

    private var value = 0

    init(start: Int) {
        self.value = start
    }

    func makeIterator() -> AnyIterator<Int> {
        return AnyIterator((0..<value).reversed().makeIterator())
    }
}

for i in Countdown(start: 3) {
    print(i)
} // print 1 2 3

必须保持状态;这就是这些功能的本质(即使在具有协同程序的世界中)。没有直接维护它没关系;只是委托一个更原始的类型。 Swift有几十个内置的迭代器可以用来构建你可能需要的大多数东西,任何迭代器都可以被提升到AnyIterator来隐藏实现细节。如果你有足够的自定义它真的需要next(),那么是的,存储状态是你的问题。有些东西必须这样做。但是我发现这一切都非常罕见,并且经常暗示它出现时会过度设计。