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

作者: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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,622评论 6 544
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,716评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,746评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,991评论 1 318
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,706评论 6 413
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,036评论 1 329
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,029评论 3 450
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,203评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,725评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,451评论 3 361
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,677评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,161评论 5 365
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,857评论 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,266评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,606评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,407评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,643评论 2 380

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,836评论 18 139
  • 生成器(Generator)可以说是在 ES2015 中最为强悍的一个新特性,因为生成器是涉及到 ECMAScri...
    Will_Wen_Gunn阅读 4,773评论 0 9
  • 我们在学习web前端的路程起步时总是疑问,我们如何更好的遍历元素呢?迭代器和生成器是什么?今天为大家带上与精彩的E...
    侬姝沁儿阅读 3,367评论 0 6
  • (一)预兆 “哗哗哗!” “只需三声,老子就让你趴下,去你妈的,不就是为了这点破钱吗?老子要是有钱,去你妈的还在这...
    李放fun阅读 661评论 9 18
  • 本来也没教我什么 我本来就是个没礼貌的人 本来就是一个冷漠的人 我的烦恼你们也永远不知道 总说我胆子小 可是你们给...
    大大树阅读 207评论 0 1