亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

在 Go 中將任意函數作為參數傳遞

在 Go 中將任意函數作為參數傳遞

Go
胡子哥哥 2022-10-10 17:48:43
我正在嘗試擴展我對 Go 函數指針的了解,并且我有一個關于在 Go 中將函數作為參數傳遞的可能性和不可能的問題。假設我想編寫一個decorator()可以包裝任何現有函數的函數。為簡單起見,讓我們將其限制為只接受一個參數并只返回一個值的函數。如果我編寫一個接受func(interface{}) interface{}作為參數的裝飾器,只要我傳入的函數也接受/返回一個interface{}類型,它就會隱式工作(請參閱 參考資料funcA)。我的問題是——有沒有辦法將現有的類型函數轉換為func(string) string類型,func(interface{}) interface{}以便它也可以傳遞給裝飾器函數,而無需將其包裝在新的匿名函數中(請參閱 參考資料funcB)?package mainimport (    "fmt")func decorate(inner func(interface{}) interface{}, args interface{}) interface {} {    fmt.Println("Before inner")    result := inner(args)    fmt.Println("After inner")    return result}func funcA(arg interface{}) interface{} {    fmt.Print("Inside A, with arg: ")    fmt.Println(arg)    return "This is A's return value"}func funcB(arg string) string {    fmt.Print("Inside B, with arg: ")    fmt.Println(arg)    return "This is B's return value"}func main() {        // This one works. Output is:    //    //   Before inner    //   Inside A, with arg: (This is A's argument)    //   After inner    //   This is A's return value    //    fmt.Println(decorate(funcA, "(This is A's argument)"))        // This doesn't work. But can it?    //fmt.Println(decorate(funcB, "(This is B's argument)"))}
查看完整描述

4 回答

?
幕布斯7119047

TA貢獻1794條經驗 獲得超8個贊

這是不可能的。原因之一是傳遞參數的機制因函數而異,使用interface{}arg 并不意味著“接受任何東西”。例如,一個以結構為參數的函數將接收該結構的每個成員,但一個以包含該結構的 interface{} 的函數將接收兩個字,一個包含結構的類型,另一個包含指向它。

因此,在不使用泛型的情況下,實現這一點的唯一方法是使用適配器函數。


查看完整回答
反對 回復 2022-10-10
?
森林海

TA貢獻2011條經驗 獲得超2個贊

使用反射包來處理具有任意參數和結果類型的函數。


func decorate(inner interface{}, args interface{}) interface{} {

    fmt.Println("Before inner")

    result := reflect.ValueOf(inner).Call([]reflect.Value{reflect.ValueOf(args)})

    fmt.Println("After inner")

    return result[0].Interface()

}

在操場上運行代碼。


與decorate問題中的函數一樣,此答案中的函數假定一個參數和一個結果。必須修改該函數以處理其他函數類型。


OP 應考慮問題中提出的匿名包裝函數與此處使用反射包之間的權衡。通過反射 API 調用函數比通過匿名包裝器調用函數慢。反射 API 也失去了類型安全性。匿名包裝函數增加了冗長性。


查看完整回答
反對 回復 2022-10-10
?
一只斗牛犬

TA貢獻1784條經驗 獲得超2個贊

作為記錄,隨著 Go 1.18 和泛型的引入,該decorator功能變得幾乎微不足道。


您可以這樣聲明類型約束:


type UnaryFunc[T any] interface {

    func(T) T

}

約束本身被參數化T以允許接受和返回任意類型的一元函數。


然后在decorate函數中使用類型參數實例化約束。簽名變為:


decorate[T any, F UnaryFunc[T]](inner F, arg T) T

多虧了類型推斷,您可以只將具體參數傳遞給函數,并且兩者T都是F明確的。


沒有命名約束的示例替代方案:


// accept and return T

decorate[T any](inner func(T) T, arg T) T


// only return T

decorate[T any](inner func() T) T


// return T and error

decorate[T any](inner func(T) (T, error), arg T) (T, error)


// N-ary function

decorate[T, U any](inner func(T, U) (T, error), argt T, argu U) (T, error)

明顯的限制是接口約束UnaryFunc只指定了只接受和返回一個 arg 類型的函數T。你不能這樣做,因為接口約束的類型集可能包括支持相同操作的類型——并且使用一個 arg 調用與使用 N 個 args 調用不兼容。


完整程序:


package main


import (

    "fmt"

)


type UnaryFunc[T any] interface {

    func(T) T

}


func decorate[T any, F UnaryFunc[T]](inner F, arg T) T {

    fmt.Println("before inner")

    result := inner(arg)

    fmt.Println("after inner")

    return result

}


func funcA(arg int) int {

    fmt.Println("inside A with:", arg)

    return arg

}


func funcB(arg string) string {

    fmt.Println("inside B with:", arg)

    return arg

}


func main() {

    // this works

    decorate(funcA, 200)

    

    // this also works

    decorate(funcB, "Func B")

}

游樂場:https ://go.dev/play/p/3q01NiiWsve


查看完整回答
反對 回復 2022-10-10
?
qq_笑_17

TA貢獻1818條經驗 獲得超7個贊

有沒有辦法將 func(string) string 類型的現有函數轉換為 func(interface{}) interface{} 類型,以便它也可以傳遞給裝飾器函數,而無需將其包裝在新的匿名函數中(見 funcB)?

不,就這么簡單:不。


查看完整回答
反對 回復 2022-10-10
  • 4 回答
  • 0 關注
  • 179 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號