美文网首页走进swift
我是否应该使用生成器(Generator)?

我是否应该使用生成器(Generator)?

作者: 梁杰_numbbbbb | 来源:发表于2016-04-14 20:25 被阅读55次

    作者:Erica Sadun,原文链接,原文日期:2016-03-17
    译者:Lanford3_3;校对:shanks;定稿:小锅

    我面临着这样的情况:我要实现一个无限的序列,它不断地来回生成区间 [min, max] 中的整数,所以最初我想的是,“让我弄个生成器(Generator)来搞定它吧”。(我并不是想讨论这个生成器写得好不好,我只想知道在这里我是不是应该使用生成器。)

    public struct UpAndDownIntGenerator: GeneratorType {
        public typealias Element = Int
        let (magnitude, period): (Int, Int)
        let minValue: Int
        var currentOffset: Int = 0
    
        public init(minValue: Int = 0, maxValue: Int) {
            assert(minValue < maxValue, "minValue must be less than maxValue")
            self.minValue = minValue
            magnitude = maxValue - minValue
            period = magnitude * 2
        }
    
        public mutating func next() -> Int? {
            let value = currentOffset % period
            let adjustedValue = value % magnitude
            let isAscending = value < magnitude
            defer { currentOffset += 1 }
            return minValue + (isAscending
                ? adjustedValue
                : magnitude - adjustedValue)
        }
    }
    

    但这个生成器永远不会停止,而且我们知道它的返回值总是非空值。所以我加了这段:

        // 这不会被输入到一个序列中
        // 所以获取下一个值并检测是否为 nil
        public mutating func fetchNextValue() -> Element {
            guard let value = next() else {
                fatalError("unable to generate next value")
            }
            return value
        }
    

    译者注: GeneratorTypenext() 方法返回的是可选值,而在本文的情境中,返回值不可能是空的,所以作者加了上面的方法来使得得到的返回值不是可选值。

    然后我就想啊:为什么要为这带有额外开销的生成器所烦扰?为什么我要创造带有生成器特质但是并不适用于序列的东西?(举个例子,对一个无限序列做 map 或 filter 操作,或者只是想办法每次取出序列中的一个值)。 所以我又写了这个:

    public struct UpAndDownProducer {
        let (magnitude, period): (Int, Int)
        let minValue: Int
        var currentOffset: Int = 0
    
        public init(minValue: Int = 0, maxValue: Int) {
            assert(minValue < maxValue, 
                "minValue must be less than maxValue") 
            self.minValue = minValue 
            magnitude = maxValue - minValue 
            period = magnitude * 2 
        }
    
        public mutating func next() -> Int {
            let value = currentOffset % period
            let adjustedValue = value % magnitude
            let isAscending = value < magnitude
            defer { currentOffset += 1 }
            return minValue + (isAscending 
                ? adjustedValue 
                : magnitude - adjustedValue)
        } 
    }
    

    所以我到底应该怎么做?非常感谢你们的建议。

    p.s. 下面是一个更简单的方法:

    public struct UpAndDownProducer {
        let minValue, maxValue: Int
        var currentValue: Int
        var direction = -1
        
        public init(minValue: Int = 0, maxValue: Int) {
            assert(minValue != maxValue,
                "No point going up and down between two equal values")
    
           // Since it starts at minValue, it's
           // going to flip immediately.
           if maxValue < minValue { direction = 1 }
           currentValue = minValue 
           (self.minValue, self.maxValue) = (minValue, maxValue) 
        } 
    
        public mutating func next() -> Int {
            defer {
                if currentValue == minValue || currentValue == maxValue {
                    direction *= -1
                }
                currentValue += direction
            }
            return currentValue
        }
    }
    

    p.p.s Davide De Franceschi 给出了他的建议,见下面的代码:

    protocol EndlessGeneratorType: GeneratorType {}
    extension EndlessGeneratorType {
        public mutating func someNext() -> Element {
            guard let element = next() else { 
                fatalError("EndlessGeneratorType must always have a next() element") 
            }
            return element
        }
    }
    

    他是这样说的:

    Davide De Franceschi: 以我浅见,最好遵从相关协议:为了现在 + 未来 + 第三方自由拓展着想

    Joe Groff 的跟帖: 如果你要做的事和 SequenceType 无关,那么使用 GeneratorType 本身并不有趣

    译者的总结:本文主要是作者希望讨论下什么时候该使用 GeneratorType。因为文中作者的需求虽然第一感觉就是用 GeneratorType 来实现,但是实际上 GeneratorTypenext() 方法返回的是可选值,要得到作者需要的非可选的返回值需要自己对 next() 返回的值进行处理才行,由此作者认为使用 GeneratorType 是多此一举,还不如写一个能直接返回非可选值的实现。作者最后的 p.p.s 中展示的意见是在协议中对 next() 返回值做处理,这样也便于未来的拓展和维护。有些跟帖和评论的意见是,脱离 SequencyType 使用 GeneratorType 没什么意义,GeneratorType 应该用在确实有必要使用的地方。
    本文由 SwiftGG 翻译组翻译,已经获得作者翻译授权,最新文章请访问 http://swift.gg

    相关文章

      网友评论

        本文标题:我是否应该使用生成器(Generator)?

        本文链接:https://www.haomeiwen.com/subject/ionglttx.html