Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Pergi |. Cara yang cekap dan boleh dibaca untuk menambahkan kepingan dan menghantar ke fungsi variadic

Pergi |. Cara yang cekap dan boleh dibaca untuk menambahkan kepingan dan menghantar ke fungsi variadic

王林
王林ke hadapan
2024-02-05 21:30:11556semak imbas

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

Kandungan soalan

Andaikan saya mempunyai saluran paip berfungsi berikut:

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...)...)
}

Oleh kerana masalah yang saya ingin selesaikan adalah diwarisi, functions 中的函数应该在 opts 中的函数之前调用,所以我不能只附加到 opts 但我必须前置 functionsopts (通过 append(functions, opts...) ),然后再次使用 ... menghantarnya ke fungsi seterusnya dalam perancangan, jadi saya mendapat ungkapan pelik:

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

Saya tidak tahu betapa cekapnya, tetapi saya pasti ia kelihatan pelik,

Mesti ada cara yang lebih baik untuk melakukan ini, itulah yang saya cari. ​​p>

Tetapi saya akan menghargai penjelasan yang disertakan tentang kecekapan :)

Sunting: Saya tidak boleh menukar jenis parameter daripada opts ...functionobject 更改为 opts []functionobject (如@dev.bmax 在评论中建议的那样),因为我在现有代码库中进行了更改,所以我无法更改调用 func{ 的函数1,2,3}

  1. Dengan "kelihatan pelik" saya bukan sekadar bermaksud "kelihatan", maksud saya melakukan ini dua kali (elips) kelihatan pelik dan kelihatan tidak cekap (adakah saya salah?)

Jawapan betul


Tambah di hadapan kepingan pada dasarnya tidak cekap kerana ia memerlukan gabungan:

  • Peruntukkan susunan sandaran yang lebih besar
  • Alihkan item ke hujung kepingan
  • …atau kedua-duanya sekali.

Adalah lebih cekap jika anda boleh menukar konvensyen panggilan antara fungsi untuk hanya menambah pilihan dan kemudian memprosesnya secara terbalik. Ini mengelakkan berulang kali mengalihkan item ke hujung kepingan dan mengelakkan semua kecuali peruntukan pertama (jika ruang yang mencukupi diperuntukkan lebih awal daripada masa).

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

Nota: func3(opts ...functionobject) / func3(opts...)func3(opts []functionobject) / func3(opts) adalah setara dalam prestasi. Yang pertama adalah gula sintaksis yang berkesan untuk memotong kepingan.

Namun, anda menyebut bahawa anda perlu mengekalkan konvensyen panggilan...

Kod contoh anda akan menghasilkan pertama, kedua, ketiga, kelima... peruntukan tambahan dalam setiap fungsi - peruntukan diperlukan untuk menggandakan saiz tatasusunan sandaran (untuk kepingan kecil). append(functions, opts...) juga boleh diperuntukkan jika lampiran awal tidak menghasilkan kapasiti ganti yang mencukupi.

Fungsi pembantu boleh menjadikan kod lebih mudah dibaca. Ia juga boleh diguna semula opts Menyokong kapasiti ganti dalam tatasusunan:

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.
}

Beberapa alternatif tanpa fungsi pembantu untuk menerangkan peruntukan dengan lebih terperinci:

// 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...)
}

Anda boleh pergi selangkah lebih jauh dan menggunakan semula kapasiti ganti dalam opts tanpa memperuntukkan kepingan yang mengandungi item yang akan ditambah (0-1 peruntukan setiap fungsi). Walau bagaimanapun, ini rumit dan terdedah kepada ralat - saya tidak mengesyorkannya.

Atas ialah kandungan terperinci Pergi |. Cara yang cekap dan boleh dibaca untuk menambahkan kepingan dan menghantar ke fungsi variadic. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:stackoverflow.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam