文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

去 |附加切片并发送到可变参数函数的高效且可读的方法

2024-02-05 22:09

关注

问题内容

假设我有以下功能管道:

func func3(opts ...functionobject) {
    for _, opt := range opts {
        opt()
    }
}

func func2(opts ...functionobject) {
   var functions []functionobject
   functions = append(functions, somefunction3)
   functions = append(functions, somefunction4)
...
...
...
    func3(append(functions, opts...)...)
}


func func1(opts ...functionobject) {
   var functions []functionobject
   functions = append(functions, somefunction)
   functions = append(functions, somefunction2)
...
...
...
    func2(append(functions, opts...)...)
}

由于我想解决的问题继承的原因, functions 中的函数应该在 opts 中的函数之前调用,所以我不能只附加到 opts 但我必须前置 functionsopts (通过 append(functions, opts...) ),然后再次使用 ... 将其发送到管道中的下一个函数,所以我得到了奇怪的表达式:

func2(append(functions, opts...)...)

我不知道它的效率如何,但我确信它看起来很奇怪,

一定有更好的方法来做到这一点,这就是我正在寻找的。

但是我很感激有关效率的附带解释:)

编辑: 我无法将参数类型从 opts ...functionobject 更改为 opts []functionobject (如@dev.bmax 在评论中建议的那样),因为我在现有代码库中进行了更改,所以我无法更改调用 func{ 的函数1,2,3}

  • 我所说的“看起来很奇怪”不仅仅指“外观”,而是说两次执行此操作(省略号)看起来很奇怪,而且效率似乎很低(我错了吗?)

  • 正确答案


    在切片前面添加基本上效率很低,因为它需要以下内容的组合:

    • 分配更大的后备数组
    • 将项目移至切片末尾
    • ...或两者兼而有之。

    如果您可以将函数之间的调用约定更改为仅附加选项,然后反向处理它们,那么效率会更高。这可以避免重复地将项目移动到切片的末尾,并避免第一个之外的所有分配(如果提前分配了足够的空间)。

    func func3(opts ...functionobject) {
        for i := len(opts) - 1; i >= 0; i-- {
            opts[i]()
        }
    }

    注意: func3(opts ...functionobject) / func3(opts...)func3(opts []functionobject) / func3(opts) 在性能上是等效的。前者是传递切片的有效语法糖。

    但是,您提到您需要保留调用约定...

    您的示例代码将导致每个函数内的第一个、第二个、第三个、第五个……附加分配 - 需要分配以使支持数组的大小加倍(对于小切片)。如果早期的附加没有创建足够的备用容量,append(functions, opts...) 也可能会分配。

    辅助函数可以使代码更具可读性。它还可以重用 opts 支持数组中的备用容量:

    func func2(opts ...functionobject) {
        // 1-2 allocations. always allocate the variadic slice containings 
        // prepend items. prepend reallocates the backing array for `opts`
        // if needed.
        opts = prepend(opts, somefunction3, somefunction4)
        func3(opts...)
    }
    
    // generics requires go1.18+. otherwise change t to functionobject.
    func prepend[t any](base []t, items ...t) []t {
        if size := len(items) + len(base); size <= cap(base) {
            // extend base using spare slice capacity.
            out := base[:size]
            // move elements from the start to the end of the slice (handles overlaps).
            copy(out[len(items):], base)
            // copy prepended elements.
            copy(out, items)
            return out
        }
        return append(items, base...) // always re-allocate.
    }
    

    一些不带辅助函数的替代选项,可以更详细地描述分配:

    // Directly allocate the items to prepend (2 allocations).
    func func1(opts ...FunctionObject) {
        // Allocate slice to prepend with no spare capacity, then append re-allocates the backing array
        // since it is not large enough for the additional `opts`.
        // In future, Go could allocate enough space initially to avoid the
        // reallocation, but it doesn't do it yet (as of Go1.20rc1).
        functions := append([]FunctionObject{
            someFunction,
            someFunction2,
            ...
        }, opts...)
        // Does not allocate -- the slice is simply passed to the next function.
        func2(functions...)
    }
    
    // Minimise allocations (1 allocation).
    func func2(opts ...FunctionObject) {
       // Pre-allocate the required space to avoid any further append
       // allocations within this function.
       functions := make([]FunctionObject, 0, 2 + len(opts))
       functions = append(functions, someFunction3)
       functions = append(functions, someFunction4)
       functions = append(functions, opts...)
       func3(functions...)
    }
    

    您可以更进一步,重用 opts 中的备用容量,而无需分配包含要前置的项目的切片(每个函数 0-1 分配)。然而,这很复杂并且容易出错——我不推荐它。

    以上就是去 |附加切片并发送到可变参数函数的高效且可读的方法的详细内容,更多请关注编程网其它相关文章!

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    软考中级精品资料免费领

    • 历年真题答案解析
    • 备考技巧名师总结
    • 高频考点精准押题
    • 资料下载
    • 历年真题
    • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

      难度     813人已做
      查看
    • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

      难度     354人已做
      查看
    • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

      难度     318人已做
      查看
    • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

      难度     435人已做
      查看
    • 2024年上半年系统架构设计师考试综合知识真题

      难度     224人已做
      查看

    相关文章

    发现更多好内容
    咦!没有更多了?去看看其它编程学习网 内容吧
    首页课程
    资料下载
    问答资讯