首頁 > 後端開發 > Golang > 去 |附加切片並發送到可變參數函數的高效且可讀的方法

去 |附加切片並發送到可變參數函數的高效且可讀的方法

王林
發布: 2024-02-05 21:30:11
轉載
649 人瀏覽過

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

問題內容

假設我有以下功能管道:

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 分配)。然而,這很複雜且容易出錯——我不推薦它。

    以上是去 |附加切片並發送到可變參數函數的高效且可讀的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    來源:stackoverflow.com
    本網站聲明
    本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
    熱門教學
    更多>
    最新下載
    更多>
    網站特效
    網站源碼
    網站素材
    前端模板