V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
dyllen
V2EX  ›  Go 编程语言

go 新特性 range over func 没怎么看懂。。。。写库的时候用吗?

  •  
  •   dyllen · 28 天前 · 7604 次点击

    go1.23 新的正式特性 range over func 看了下没怎么看懂。。。

    for range 返回一个函数,函数里面还是 for range 或者就 for 循环:

    func Backward[E any](s []E) func(func(int, E) bool) {
        return func(yield func(int, E) bool) {
            for i := len(s) - 1; i >= 0; i-- {
                if !yield(i, s[i]) {
                    return
                }
            }
            return
        }
    }
    
    func main() {
        sl := []string{"hello", "world", "golang"}
        for i, s := range Backward(sl) {
            fmt.Printf("%d : %s\n", i, s)
        }
    }
    

    Backward这个函数这形式看起来还真有点复杂,不好理解。。。。可能例子不是相应场景下需要的,看半天没理解。 感觉一般都用不上。

    第 1 条附言  ·  27 天前
    示例是从一个博客文章里面复制的
    第 2 条附言  ·  26 天前
    一时没看出来 yield 就是函数类型参数变量的名称,一眼看以为是啥关键字..... 测试了下,每次执行到 yield(x,x)的时候就会返回值给 range 输出,接着继续执行 yield(x,x)后面的代码行。
    104 条回复    2024-08-20 16:57:46 +08:00
    1  2  
    BBCCBB
        1
    BBCCBB  
       28 天前
    python 里的生成器? 不过 python 不需要 fn 里再套一个 fn.
    dyllen
        2
    dyllen  
    OP
       28 天前
    @BBCCBB 所以看起来有点费解,迭代器,应该是一种东西。
    Leviathann
        3
    Leviathann  
       28 天前   ❤️ 11
    这语法笑死
    go 在探索下限这点上上限很高
    lysShub
        4
    lysShub  
       28 天前
    可不要要写这种垃圾代码,这特性怎么通过的的,太傻逼了
    sagaxu
        5
    sagaxu  
       28 天前   ❤️ 2
    大道至简没能一以贯之啊,特性越加越多,以后不敢说看 Go 代码时语言层面的心智负担低了
    dyllen
        6
    dyllen  
    OP
       28 天前   ❤️ 2
    迭代器是一个函数,它将一个序列中的连续元素传递给一个回调函数,通常称为"yield"。迭代器函数会在序列结束或者 yield 回调函数返回 false(表示提前停止迭代)时停止。
    wunonglin
        7
    wunonglin  
       28 天前   ❤️ 1
    就是迭代器。和 js 里的 yield 差不多,但是语法真的难看
    ZxykM
        8
    ZxykM  
       28 天前   ❤️ 4
    我不敢相信这特性怎么通过的?写法也太丑了
    dyllen
        9
    dyllen  
    OP
       28 天前
    @wunonglin 说实话,其他语言的语法更好理解
    dyllen
        10
    dyllen  
    OP
       28 天前
    @ZxykM 这个就是那个 rsc 自己提的提案,能不通过吗? https://github.com/golang/go/issues/61405
    body007
        11
    body007  
       28 天前
    我喜欢 go ,但我讨厌这个特性。主要场景也不是你上面那种封装完立即 range 遍历,而是生成一个迭代器,放到代码其他位置去遍历,这种代码容易写出 bug ,还不好定位。你可以看下 iter 包,里面还有 iter.Pull 这种方法,就是把迭代器变成 next() 和 stop() 两个函数,意思是在其他代码位置一个个读取迭代器元素,最好在最上面写 defer stop()。go 容易阅读是很多使用者喜欢的原因,难以想象到处充斥这种迭代器的代码的阅读体验。生成迭代器和使用迭代器的代码位置可能相差十万八千里,调试打断点估计都不好找位置🤣
    zhuangzhuang1988
        12
    zhuangzhuang1988  
       28 天前
    PTLin
        13
    PTLin  
       28 天前
    看来不止我一个觉得丑,之前在一个群喷这个语法,一个大佬搬出了一堆文章和 x 上的博客证明这个 push 的迭代器多么先进。
    pkoukk
        14
    pkoukk  
       28 天前
    这迭代器太丑了,没眼看
    assassing
        15
    assassing  
       28 天前
    函数签名 3 个 func 看傻了,要是 2 个还能接受
    ninjashixuan
        16
    ninjashixuan  
       28 天前
    真真的太丑了,之前提案阶段就一堆人吐槽了。
    dbpe
        17
    dbpe  
       28 天前
    问题来了...这 shit 的提案..怎么过的?
    dyllen
        18
    dyllen  
    OP
       28 天前
    @dbpe go 是 Google 主导,他们自己的提案肯定会通过的,外面的人反对无效。
    Donahue
        19
    Donahue  
       28 天前
    喜欢 go 语言,交叉编译二进制运行很好。但是语法真的丑。
    Jinnrry
        20
    Jinnrry  
       28 天前
    我以前都会吹 go 大道至简,代码易读。但是看到这玩意,我以后不吹了
    assassing
        21
    assassing  
       28 天前
    按照写 Python 的经验,迭代器没有非用不可的场景,虽然它很简单。

    Go 语言中宁可牺牲简单性,也不愿意引入新关键字,我是理解不了的。去原帖看了下,作者认为这样实现迭代器工作量比较小...
    hxtheone
        22
    hxtheone  
       28 天前 via iPhone
    个人感觉 Go 语言一直对库的调用者比编写者更友好, 但是这个特性在写库的时候简直是太磨人了, 配上泛型和又臭又长的匿名函数简直是没眼看
    kiripeng
        23
    kiripeng  
       28 天前   ❤️ 1
    这特性就是一坨,我都懒得升级
    dyllen
        24
    dyllen  
    OP
       28 天前
    @assassing 不也增加了 yield 关键字吗,把复杂性丢给我使用者。。。
    KaynW
        25
    KaynW  
       28 天前
    确实丑的可以,层级这么多,}}}}看着丑的很
    loveuer
        26
    loveuer  
       28 天前
    @dyllen #24 没增加,yield 是个匿名函数
    dyllen
        27
    dyllen  
    OP
       28 天前
    @loveuer `func(yield func(int, E) bool)`这里面的这个 yield 看起来不像函数呀。
    cmdOptionKana
        28
    cmdOptionKana  
       28 天前
    挺好的啊,Go 的这个函数就像一个外皮透明的动物,让你一眼看见里面的食道和胃具体是怎样消化食物的,非常直白。写 Go 真的让人头脑清晰,而且过一段时间回头看也条理清晰,比一堆语法糖好多了。
    stevenshuang
        29
    stevenshuang  
       28 天前
    @dyllen yield 没有增加,yield 就是那个函数的命名。https://go.dev/ref/spec#Keywords
    leonshaw
        30
    leonshaw  
       28 天前
    @dyllen yield 是参数名。这东西看起来就是 for-range 简单地帮你生成了一个回调函数。
    it := Backward(sl)
    cb := func(i int, s string) bool {
    fmt.Printf("%d : %s\n", i, s)
    return true
    }
    it(cb)
    vfs
        31
    vfs  
       28 天前   ❤️ 3
    @sagaxu 感觉从支持泛型的那一版就已经走下坡路了.
    assassing
        32
    assassing  
       28 天前
    @cmdOptionKana 噗,你这比喻很到位,大中午的不想吃饭了都


    @dyllen 不是关键字,随便叫什么都成,关键字你可以查看这里: https://github.com/golang/go/blob/release-branch.go1.23/src/builtin/builtin.go
    yqf0215
        33
    yqf0215  
       28 天前
    @PTLin 这个特性,也被大佬抨得一塌糊涂,太复杂了。
    这个特性除了炫耀自己编程厉害,并没有什么特殊的好处,其功能用其他方式同样能完成,还容易读。说与其弄这些,不如提升性能。
    MoYi123
        34
    MoYi123  
       28 天前
    Backward 里的 func 套 func 是函数式编程里的常见的惰性求值的写法. 不觉得有什么问题.
    ChrisFreeMan
        35
    ChrisFreeMan  
       28 天前
    我觉得还好,多看几眼觉得很合理。
    sophos
        36
    sophos  
       28 天前
    我感觉这个特性是很有用的,主要是用在提供常用操作的组件中,调用方的代码还是比较直观的

    至于写组件的时候,可以直接用 iter.Seq 或 iter.Seq2 ,别直接用匿名函数就行,还好吧
    dacapoday
        37
    dacapoday  
       28 天前
    高阶函数而已,比较取巧,但合理。
    Nazz
        38
    Nazz  
       28 天前   ❤️ 1
    笑了, 这么多人又菜又爱喷

    经常写数据结构的人不会对这个接口感到陌生:

    ```go
    type Ranger[K comparable, V any] interface {
    Range(k K, v V) bool
    }
    ```

    有了 range over func 语法糖, 可以非常方便地返回上层函数
    Trim21
        39
    Trim21  
       28 天前 via Android
    golang 为了不加这个 yield 关键词真是煞费苦心…

    这个新功能还有个离谱的地方是标准库里 iter.Pull 的实现方法,直接魔改运行时…
    dule
        40
    dule  
       28 天前
    以前看过 go 语言的代码就觉得这玩意语法是真丑陋啊,太不直观了
    28Sv0ngQfIE7Yloe
        41
    28Sv0ngQfIE7Yloe  
       28 天前
    有点炫技啊, 理解起来有一定的心智负担
    zhwq
        42
    zhwq  
       28 天前
    看来又要换坑了,就像早年 PHP 支持 namaspace ,之后就是一日不一日。
    语言有没有变的更高级我不知道,我只知道,用的越来越少了
    cheng6563
        43
    cheng6563  
       28 天前
    幸好泛型没用()
    cmdOptionKana
        44
    cmdOptionKana  
       28 天前
    @Morii 这是一板一眼最“老实”的笨办法的写法,与炫技毫无关系。
    me1onsoda
        45
    me1onsoda  
       28 天前
    go 的源码不是本来就很难看吗。。。
    dyllen
        46
    dyllen  
    OP
       28 天前
    @cmdOptionKana 菜鸡还真看不出来,看着就挺复杂的。
    LieEar
        47
    LieEar  
       28 天前
    func Backward[E any](s []E) func(func(int, E) bool) {
    return func(yield func(int, E) bool) {

    眼前一黑
    cmdOptionKana
        48
    cmdOptionKana  
       28 天前
    @dyllen 什么菜不菜的,不习惯而已。

    就像 react 、vue 刚出来的时候,一堆用习惯了 jQuery 的人说 react 复杂,等 react 流行后,又反过来一堆人说 jquery 啰嗦、乱。其实就是看你习惯哪个。
    bv
        49
    bv  
       28 天前
    @LieEar 能理解 Java/JavaScript 中的 stream.filter(v -> v !=10 ) 应该对这种形式好理解了。(只是这种高阶函数的形式互通,不是说 go iter 和他们的 stream 功能类似)
    CHTuring
        50
    CHTuring  
       28 天前
    其实看起来还行吧,就是返回个 yield 的迭代器,不过一般也用不上。
    Rehtt
        51
    Rehtt  
       28 天前
    emmm 感觉也不是很难理解啊
    Nazz
        52
    Nazz  
       28 天前
    @Morii 这样好理解吗

    package main

    type Vector[T any] []T

    func (c Vector[T]) Range(f func(i int, v T) bool) {
    for i := 0; i < len(c); i++ {
    f(i, c[i])
    }
    }

    func main() {
    var vec = Vector[int]{1, 3, 5, 7, 9}
    for i, v := range vec.Range {
    println(i, v)
    }
    }
    Nazz
        53
    Nazz  
       28 天前
    package main

    type Vector[T any] []T

    func (c Vector[T]) Range(f func(i int, v T) bool) {
    for i := 0; i < len(c); i++ {
    if !f(i, c[i]) {
    return
    }
    }
    }

    func main() {
    var vec = Vector[int]{1, 3, 5, 7, 9}
    for i, v := range vec.Range {
    println(i, v)
    }
    }
    Elaina
        54
    Elaina  
       28 天前   ❤️ 3
    Golang 自从出了泛型之后就越来越依托答辩了,之前嘴硬说为了简洁,把代码搞得又臭又长,现在又开始整活
    dyllen
        55
    dyllen  
    OP
       28 天前
    @CHTuring
    @Rehtt 实在没想出来平时哪里用得上,解决了我的什么问题导致不好理解,套了好几层的感觉。
    cmdOptionKana
        56
    cmdOptionKana  
       28 天前
    @Elaina 我怀疑你脱离事实硬黑。对泛型有需求的代码,现在用泛型,代码比以前用 interface 模拟泛型的代码更长?
    Elaina
        57
    Elaina  
       28 天前
    @cmdOptionKana 你没看到我说的是之前么,我说之前为了简洁搞得代码又臭又长
    cmdOptionKana
        58
    cmdOptionKana  
       28 天前
    @Elaina

    > Golang 自从出了泛型之后就越来越依托答辩了

    你说出了泛型之后,越来越差。但泛型比用 interface 模拟好,不知道为什么评价为“越来越”差。
    Elaina
        59
    Elaina  
       28 天前   ❤️ 1
    @cmdOptionKana 这泛型的方括号你就说是不是依托答辩,而且最开始本身宣传的就是不过度设计,极简,现在不少新特性都跟之前的理念相悖
    cmdOptionKana
        60
    cmdOptionKana  
       28 天前   ❤️ 1
    @Elaina 目标是尽量简单,现在虽然没有做到极致完美,但总的来说确实已经非常克制了,也没增加多少复杂的东西。

    泛型也是社区每年调查,每年很多人提需求才增加的,也就是说,不加泛型一堆人骂,加了泛型也有人骂,神来了也没有更好的处理方法啊。你是希望不加泛型还是你有更好的泛型方案?
    Elaina
        61
    Elaina  
       28 天前   ❤️ 1
    @cmdOptionKana 我没说不能加泛型啊,写 parser 别偷懒用方括号啊
    EAFA0
        62
    EAFA0  
       28 天前
    ``` golang
    EAFA0
        63
    EAFA0  
       28 天前
    研究了一下... 你把这块代码这样看:

    ``` golang
    func Backward[E any](s []E) func(func(int, E) bool) {
    return func(yield func(int, E) bool) {
    for i := len(s) - 1; i >= 0; i-- {
    if !yield(i, s[i]) {
    return
    }
    }
    return
    }
    }

    func main() {
    sl := []string{"hello", "world", "golang"}

    iter := Backward(sl)

    iter(func(index, value) bool {
    fmt.Printf("%d : %s\n", i, s)
    return true
    })
    }
    ```

    是等效的, 不知道这个能不能帮助你理解, 实际上这个新特性看起来只是加了个编译时替换 (可能描述的不准确)
    cmdOptionKana
        64
    cmdOptionKana  
       28 天前   ❤️ 1
    @Elaina

    你说“越来越依托答辩”、不简洁、“代码搞得又臭又长”、“现在又开始整活”

    结果你真正想表达的却是“不喜欢方括号”。你一开始的原话与你最终的表达相差太大了。
    laikick
        65
    laikick  
       28 天前
    @cmdOptionKana 不管怎么说. range over func 确实感觉破坏了 golang 代码易读这个优点
    Elaina
        66
    Elaina  
       28 天前   ❤️ 1
    @cmdOptionKana 这几点不冲突啊,我不喜欢方括号是因为可读性差啊,parser 又不是不能处理尖括号,所以我说它是依托答辩啊,你要搞就好好搞呗,搞了但又搞成依托答辩,以及不简洁不代表代码会又臭又长,简洁了不代表可读性就好
    QXDM
        67
    QXDM  
       28 天前   ❤️ 1
    @cmdOptionKana #28 能清晰的看到依托 shit 的组成与产出
    cmdOptionKana
        68
    cmdOptionKana  
       28 天前
    @laikick 这是个很普通的特性,也很直白,觉得不易读大概率是之前类似的写法看得比较少,不习惯而已。
    cmdOptionKana
        69
    cmdOptionKana  
       28 天前
    @QXDM

    A. 能看清 shit 的产生过程
    B. 看不清过程,反正产生 shit

    作为写代码解决问题的人,你认为哪个好?
    cmdOptionKana
        70
    cmdOptionKana  
       28 天前
    @Elaina 你这一段很混乱,我理不清其中的逻辑,因此无法反驳。
    vfs
        71
    vfs  
       28 天前
    @cmdOptionKana 如果一开始 golang 就支持了泛型, 我可能根本不会去学它, 因为我完全可以去学习更牛逼的 Java 。 我之所以喜欢他, 是因为它有着像 c 一样简单的语法。 如今 golang 只是在慢慢的编程另外一个 java 而已, 而这个世界根本不需要另外一个 java 语言。这也就是为为什么对它支持泛型不太看好。今天能支持泛型, 明天就能支持异常。
    cmdOptionKana
        72
    cmdOptionKana  
       28 天前
    @vfs golang 从来没有承诺不添加泛型。事实上在没有泛型的时候,确实有很多人不得不用 interface 来模拟泛型,这个需求是真实存在的。添加泛型后,社区文化也是尽量控制泛型的使用范围,能不用就不用,只在有明显帮助的时候才用。就目前来看还是比较乐观的,泛型不会蔓延得太厉害。
    0x67cq
        73
    0x67cq  
       28 天前
    @EAFA0 嗯,看你这个例子就感觉比较好理解,最上面那个很难理解的点在于 range 本身是一个语法糖。我没办法直接看出来,它构造了一个什么 yield 函数作为参数传入 Backword 执行后返回的结果函数用于执行。你这里把 `for k,v := range iter` 改写成 `iter ( func()bool{} return true)`这种方式就很明确。相当于把语法糖解开了。总的来说我不喜欢这个语法糖。
    0x67cq
        74
    0x67cq  
       28 天前
    @EAFA0 我读上面最开始卡到的地方其实就是,for k,v := range iter{fmt.Printf...} 这里,我无法理解 生成的 iter 是怎么得到 yield 这个参数函数的。简单来说就是,TMd 的哪来的 yield 。
    victorc
        75
    victorc  
       28 天前   ❤️ 1
    谁 tm 提的,这人是个纯 SB

    c++被弄成现在这个鸟样就是有这群鸟人把持标准委员会
    kk2syc
        76
    kk2syc  
       28 天前
    大伙要像果粉学习,自适应很重要!丑归丑,能用好用性能好就行了……
    kingcanfish
        77
    kingcanfish  
       28 天前
    @0x67cq #74 而且 yield 返回值是个 bool 值 tmd 拿来的布尔值 tmd 到底怎么跳出迭代 晕了
    vfs
        78
    vfs  
       28 天前
    @cmdOptionKana "很多人不得不用 interface 来模拟泛型": 其实我并不觉得这是什么问题, 就像在 c 里面用 void* 来指向任何地址的做法一样。
    EAFA0
        79
    EAFA0  
       28 天前
    @0x67cq 我一开始也很奇怪, 搓了几段代码后发现这个 for range 跟 py 那种 yield 语法可以说是没有一点关联性... 这个语法糖远不如直接调用直观, 有一点关联性的是 iter 包下面的 Pull / Pull2 方法, 这两个方法的效果才跟其他语言的迭代器有点类似
    MoYi123
        80
    MoYi123  
       28 天前
    @Nazz 如果这个数据结构基于硬盘, 提供的接口是 async 的, 这种传入回调函数的方式可以大幅减少 await 的次数, 性能会比 await iter.next() 好很多.

    虽然 go 里用不到这个.
    sagaxu
        81
    sagaxu  
       28 天前
    @vfs 之前说好的 Go 不需要泛型,结果加了,加了迭代器之后,也许异常也会加进去,defer+异常+panic 组合下的坑够博主们写 100 篇了。这次加的迭代器,易读性比 Java 和 Python 都低不少,参数是函数还返回函数的高阶函数,倒不是说理解不了,只不过读到那里思路卡顿一下才过得去。

    “不直观”程度跟经典的 signal 函数定义有的一拼
    void ( *signal(int signum, void (*handler)(int)) ) (int);
    voidmnwzp
        82
    voidmnwzp  
       28 天前   ❤️ 1
    这种傻逼代码都能作为官方示例?看来 golang 药丸
    bugfan
        83
    bugfan  
       28 天前
    @voidmnwzp #82 8 月初,golang 技术主管换人了。感觉要走下坡路了😭
    bugfan
        84
    bugfan  
       28 天前
    label
        85
    label  
       28 天前   ❤️ 4
    xz410236056
        86
    xz410236056  
       28 天前
    这不就是返回一个闭包(高阶函数)其他语言叫生成器、装饰器。这不是很正常的语法吗?
    xz410236056
        87
    xz410236056  
       28 天前
    @assassing #21 学两天 swift 就老实了,关键字多到可以出一本书了,完全就是两个极端
    vipfts
        88
    vipfts  
       28 天前
    @Nazz #38 虽然我看不懂, 但给你点赞就对了
    assassing
        89
    assassing  
       28 天前
    @xz410236056 #87 那肯定 Go 更好。

    只不过这个迭代器实现太难用,支持了和没支持一样。像同时另一个 range 改进:for i := range n 替代 for i := 0; i < n; i++,我就觉得挺好,当然肯定也有人喷糖撒过头了。
    lxdlam
        90
    lxdlam  
       28 天前   ❤️ 7
    首先是一个时间线:
    - 一个 standard 的 iterator 是一个社区一直在讨论跟推进的,正式的 proposal 在 2022 年就已经成型并进行了广泛讨论: https://github.com/golang/go/discussions/54245
    - rsc 选择了使用 push-func 来实现的讨论,针对于他对*既有* stdlib 的代码的观察 https://github.com/golang/go/discussions/56413
    - 关于最终加入 spec 的讨论,从 go1.22 到 1.23 release ,数百条关于实现方式、问题、语法选择的讨论都可以在 https://github.com/golang/go/issues/61405 看到。

    在这条帖子的讨论中,mix 了非常多的对于不同方向的讨论:
    - 该不该加入 Iterator ?这个问题相信毋庸置疑,一个标准化的 iterator 定义能够极大方便用户和库开发者。举个非常简单的例子,我们可以将 `sql.Rows` 传入一个 `slices.Chunk` 函数中,使得只需要
    ```
    for batch := range slices.Chunk(sql.Rows() ,6) {
    // do something with current batch
    }
    ````
    就能非常简单将 sql 返回的结果分批处理。类似的场景在各种延迟计算、数据获取、基于数据流向的调度中非常常见。
    - 该不该使用 push-func 实现? push-func 跟 pull-func 虽然在表达能力上是一致的,但是 rsc 认为:
    > Although push and pull functions are duals, they have important differences. Push functions are easier to write and somewhat more powerful to invoke, because they can store state on the stack and can automatically clean up when the traversal is over. That cleanup is made explicit by the stop callback when converting to the pull form.
    push-func 更容易转成 pull-func ,且更容易实现。当然,直观程度上,push-func 是弱于 pull-func 的,这导致了这次语法看起来更加奇怪。(来自于 Java 的例子是,stdlib 对 iterator 的实现是 pull based 的: https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/Iterator.html
    - 该不该使用 rangefunc 实现 iterator ?这个是社区最核心的讨论点之一。援引一个老哥的文章: https://www.gingerbill.org/article/2024/06/17/go-iterator-design/,社区核心始终认为,这种函数破坏了既有用户群体对这个语言的 philosophy (宗旨?方法论?不知道怎么去定义这个词比较好)的认知,一部分群体认为 Go 就是个基于过程的语言,不应该如此“函数式”;同样也有人认为,这种基于完全隐式的控制流(依赖 function stack frame 控制生命周期,range Func 完全依靠 compiler 重写)是引入新的 Bug 的万恶之源。

    就我个人来说,我并不赞成使用这种方式实现,如果有一个编译器可以认知的新的 iterator 对象,那无论从实现还是使用角度都会简单很多,而且就 go 整个语言的发展历史来看,为了一些之前没考虑到的事情,动态开洞擦屁股这种事,也不是发生一次两次了。但是这条回复的目的还是给大家一个清晰的认知,也建议讨论设计问题前,先搞清楚前因后果时间脉络,减少鸡同鸭讲。
    povsister
        91
    povsister  
       28 天前
    #90
    概括的非常好了,虽然你可以说 go 是 googelang ,因为他确实也塞了很多私货,甚至按照 k8s 的需求定制特性也不是没有先例。

    但观察 go 社区的讨论,你会发现没人像 V2EX 发言一样:随性发表观点看法而不带任何论据。
    甚至 rsc 本人也会对你提出的疑问认真回复(有幸被回复过,只能说大佬看问题的 level 就是不一样)

    与其去喷泛型,喷 iter ,不如看看你的使用场景到底有没有必要用到这些特性。
    作为一个内部基础库维护者,我真得感谢泛型替我省了不少 garbage 代码和注释的口水,编译器保证比我额外加一堆检查要省事的多,而且 iter 本身,在重数据处理的流程中也是非常好用的特性。

    你的业务场景用不到,那就不用。没必要跳出来喷一句:go 的语言设计就是一坨屎山。
    如果你有意见,请积极参与到社区标准落地中去,或者,zig ,请。
    FYFX
        92
    FYFX  
       28 天前
    https://go.dev/wiki/RangefuncExperiment ,OP 应该在一开始就把官方给的例子贴全了
    说起来要是我理解的没问题
    for i,s := range Backward(sl){...} 这里的 i,s 不是从 Backward 函数返回的,更像是给回调函数声明用的?然后编译器再往回调函数里面补个 return true?
    james122333
        93
    james122333  
       28 天前 via Android
    我也觉得这过于複杂
    james122333
        94
    james122333  
       28 天前 via Android
    @vfs

    有泛型还是挺省力的 不用写太多 switch type 和反射 异常有 panic recover 即可不用太多东西
    jiangzm
        95
    jiangzm  
       28 天前
    语法过于丑漏
    DOLLOR
        96
    DOLLOR  
       28 天前
    把 OP 的 go 翻译成 TS ,大概是这样子的

    const Backward = <E>(s: E[]): (myYield: (i: number, e: E) => boolean) => void => {
    ⬜return (myYield: (i: number, e: E) => boolean) => {
    ⬜⬜for (let i = s.length - 1; i >= 0; i -= 1) {
    ⬜⬜⬜if (!(myYield(i, s[i]))) {
    ⬜⬜⬜⬜return
    ⬜⬜⬜}
    ⬜⬜}
    ⬜⬜return
    ⬜}
    }

    const main = () => {
    ⬜const sl = ["hello", "world", "golang"]
    ⬜forRange(Backward(sl), (i, s) => {
    ⬜⬜console.log(`${i} : ${s}`)
    ⬜⬜return true
    ⬜})
    }

    // 模拟 for range 的行为
    const forRange = <E>(
    ⬜iter: (myYield: (i: number, e: E) => boolean) => void,
    ⬜body: (i: number, s: E) => boolean
    ) => {
    ⬜iter(body)
    }

    main()


    也就是说,迭代器并没有增加任何新的语法,就是单纯满足签名为 func(func(int, E) bool)的函数。
    你说它繁杂、丑陋嘛,确实,要写大量的模板代码。
    但你说它大道至简嘛,确实,没有增加任何新的语法。😂
    collery
        97
    collery  
       28 天前
    不好意思,方法签名数括号没数过来~~~
    低智 javaer
    mark2025
        98
    mark2025  
       28 天前
    @DOLLOR ts 看起来似乎顺眼些~
    Trim21
        99
    Trim21  
       27 天前
    @Elaina #61 那 map[string]int 要不要改尖括号啊?
    yumenaka
        100
    yumenaka  
       27 天前 via Android
    作为一个调库的低水平用户,range over func 让我少写了不少模板代码。
    因为调库的地方,总是比写库的地方多,我的项目的阅读难度其实是下降了。

    复杂性从来都不会消失,只是被封装。
    抱怨第三方库会因此变难的用户,技能应该比我熟练,经常阅读第三方库。但做的工作,又没有涉及更底层的部分吧。

    至于语法与格式……
    用 go 的人,不是从一开始就接受了 go fmt 吗?
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1026 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 18:50 · PVG 02:50 · LAX 11:50 · JFK 14:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.