Go |. Effiziente und lesbare Möglichkeit, ein Slice anzuhängen und an eine Variadic-Funktion zu senden

王林
Freigeben: 2024-02-05 21:30:11
nach vorne
609 Leute haben es durchsucht

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

Frageninhalt

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...)...)
}
Nach dem Login kopieren

Da das Problem, das ich lösen möchte, vererbt wird, sendet functions 中的函数应该在 opts 中的函数之前调用,所以我不能只附加到 opts 但我必须前置 functionsopts (通过 append(functions, opts...) ),然后再次使用 ... es an die nächste Funktion in der Pipeline, sodass ich den seltsamen Ausdruck erhalte:

func2(append(functions, opts...)...)
Nach dem Login kopieren

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.

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}

nicht ändern
  • Mit „sieht seltsam aus“ meine ich nicht nur „sieht aus“, ich meine, dass es seltsam aussieht und ineffizient erscheint, wenn man das zweimal macht (Ellipsen) (liege ich da falsch?)

  • Richtige Antwort


    Das Hinzufügen vor einem Slice ist grundsätzlich ineffizient, da es eine Kombination aus Folgendem erfordert:

    • Weisen Sie ein größeres Backing-Array zu
    • Element an das Ende des Slice verschieben
    • …oder beides.

    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]()
        }
    }
    Nach dem Login kopieren

    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.
    }
    
    Nach dem Login kopieren

    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...)
    }
    
    Nach dem Login kopieren

    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!

    Quelle:stackoverflow.com
    Erklärung dieser Website
    Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
    Beliebte Tutorials
    Mehr>
    Neueste Downloads
    Mehr>
    Web-Effekte
    Quellcode der Website
    Website-Materialien
    Frontend-Vorlage