Heim > Artikel > Backend-Entwicklung > Go |. Effiziente und lesbare Möglichkeit, ein Slice anzuhängen und an eine Variadic-Funktion zu senden
Angenommen, ich habe die folgende funktionale Pipeline:
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...)...) }
Da das Problem, das ich lösen möchte, vererbt wird, sendet functions
中的函数应该在 opts
中的函数之前调用,所以我不能只附加到 opts
但我必须前置 functions
到opts
(通过 append(functions, opts...)
),然后再次使用 ...
es an die nächste Funktion in der Pipeline, sodass ich den seltsamen Ausdruck erhalte:
func2(append(functions, opts...)...)
Ich weiß nicht, wie effizient es ist, aber ich bin sicher, es sieht seltsam aus,
Es muss einen besseren Weg geben, das zu tun, das ist es, wonach ich suche. p>
Aber ich würde mich über die begleitende Erklärung zur Effizienz freuen :)
Bearbeiten:
Ich kann den Parametertyp von opts ...functionobject
更改为 opts []functionobject
(如@dev.bmax 在评论中建议的那样),因为我在现有代码库中进行了更改,所以我无法更改调用 func{ 的函数1,2,3}
Das Hinzufügen vor einem Slice ist grundsätzlich ineffizient, da es eine Kombination aus Folgendem erfordert:
Es wäre effizienter, wenn Sie die Aufrufkonvention zwischen Funktionen ändern könnten, um nur Optionen anzuhängen und sie dann in umgekehrter Reihenfolge zu verarbeiten. Dies vermeidet das wiederholte Verschieben von Elementen an das Ende des Slice und vermeidet alle bis auf die erste Zuweisung (sofern im Voraus genügend Speicherplatz zugewiesen wird).
func func3(opts ...functionobject) { for i := len(opts) - 1; i >= 0; i-- { opts[i]() } }
Hinweis: func3(opts ...functionobject) / func3(opts...)
和 func3(opts []functionobject) / func3(opts)
sind in der Leistung gleichwertig. Ersteres ist ein effektiver syntaktischer Zucker zum Übergeben von Slices.
Sie haben jedoch erwähnt, dass Sie die Aufrufkonvention beibehalten müssen...
Ihr Beispielcode führt zu ersten, zweiten, dritten, fünften ... zusätzlichen Zuweisungen innerhalb jeder Funktion – Zuweisungen sind erforderlich, um die Größe des Hintergrundarrays zu verdoppeln (für kleine Slices). append(functions, opts...)
kann auch zugewiesen werden, wenn frühere Anhänge nicht genügend freie Kapazität geschaffen haben.
Hilfsfunktionen können Code lesbarer machen. Es ist auch wiederverwendbar opts
Unterstützt freie Kapazität in Arrays:
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. }
Einige Alternativen ohne Hilfsfunktionen, um die Zuordnung genauer zu beschreiben:
// 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...) }
Sie können noch einen Schritt weiter gehen und die freie Kapazität in opts
wiederverwenden, ohne den Slice mit den voranzustellenden Elementen zuzuweisen (0-1 Zuweisung pro Funktion). Dies ist jedoch aufwendig und fehleranfällig – ich empfehle es nicht.
Das obige ist der detaillierte Inhalt vonGo |. Effiziente und lesbare Möglichkeit, ein Slice anzuhängen und an eine Variadic-Funktion zu senden. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!