假設我有以下功能管道:
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
但我必須前置 functions
到opts
(透過append(functions, opts...)
),然後再使用 ...
將其發送到管道中的下一個函數,所以我得到了奇怪的表達式:
func2(append(functions, opts...)...)
我不知道它的效率如何,但我確信它看起來很奇怪,
一定有更好的方法來做到這一點,這就是我正在尋找的。 p>
但是我很感激有關效率的附帶解釋:)
編輯:
我無法將參數類型從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中文網其他相關文章!