以Go语言为例,如何将数组作为参数传递并解包?

178
在Python和Ruby中,有一个用于将数组解包为函数参数的splat运算符(*)。在Javascript中,可以使用.apply()函数实现类似功能。在Go语言中是否有一种将数组/切片解包为函数参数的方法?如果有相关资源,那就太好了!

以下是示例:

func my_func(a, b int) (int) {
    return a + b
}

func main() {
    arr := []int{2,4}
    sum := my_func(arr)
}

15
首先,arr 不是一个数组,而是一个切片。 - newacct
4个回答

270
你可以使用类似于C语言的可变参数语法:

你可以使用类似于C语言的可变参数语法:

package main
import "fmt"

func my_func( args ...int) int {
   sum := 0
   for _,v := range args {
      sum = sum + v
   }

   return sum;
}

func main() {
    arr := []int{2,4}
    sum := my_func(arr...)
    fmt.Println("Sum is ", sum)
}

现在你可以对任意数量的事物进行求和。注意,当你调用my_func函数时,在后面加上重要的...

运行示例:http://ideone.com/8htWfx


1
...适用于slices。我们是否有类似的东西适用于类型array - Yogesh
5
@Yogesh,我可能有点晚了,但你总是可以像这样做:arr := [3]int{1, 2, 3}; my_func(arr[:]...),将其转换为切片,然后展开它。 - Kyle Anderson
8
当函数期望...interface{}类型参数而我们的参数是另一种类型时,我们该如何处理。 - Mahdi mehrabi

19

要么你的函数是可变参数,那么你可以像Hunter McMillen所示一样使用带有...符号的切片,或者你的函数有固定数量的参数,在编写代码时可以展开它们。

如果你真的想在具有固定数量参数的函数上动态执行此操作,可以使用反射:

package main
import "fmt"
import "reflect"

func my_func(a, b int) (int) {
    return a + b
}

func main() {
    arr := []int{2,4}
    var args []reflect.Value
    for _, x := range arr {
        args = append(args, reflect.ValueOf(x))
    }
    fun := reflect.ValueOf(my_func)
    result := fun.Call(args)
    sum := result[0].Interface().(int)
    fmt.Println("Sum is ", sum)
}

我很好奇这个方法与可变参数的方法相比较的性能如何。我认为它的效率会低一些,但我需要进一步研究。 - Hunter McMillen
@HunterMcMillen 你有关于性能比较的任何见解吗? - AkiRoss
@AkiRoss 可能吧,但是那是很久以前的事情了,我已经忘记结果了 :| - Hunter McMillen
1
好的,没关系:我想它可能会更慢,也许我会自己运行一些测试。谢谢。 - AkiRoss
sum := result[0].Interface().(int) --> 这个语法是做什么的? - Krishna ps
@Krishnaps:查看reflect.ValueCall()方法文档。它返回一个Value切片,对应于Go中的多个返回值。result就是这个Value切片。假设我们只处理返回一个值的函数,我们只需要取第一个值(result[0]),调用Interface()方法获取reflect.Value的底层值,然后将其断言为我们期望的返回类型(在本例中为int)。 - newacct

-4

https://play.golang.org/p/2nN6kjHXIsd

我有一个理由从一个map[string]string中解包一些带单引号的变量,也有一些不带。这是它的逻辑,上面的play链接有完整的工作片段。

func unpack(a map[string]string) string {

var stmt, val string
var x, y []string
for k, v := range a {
    x = append(x, k)
    y = append(y, "'"+v+"'")
}

stmt = "INSERT INTO tdo.rca_trans_status (" + strings.Join(x, ", ")
val = ") VALUES (" + strings.Join(y, ",") + ");"

return stmt + val}

这将干净地呈现为 MSSQL 查询:

INSERT INTO tdo.rca_trans_status (rca_json_body, original_org, md5sum, updated, rca_key) VALUES ('blob','EG','2343453463','2009-11-10 23:00:00','prb-180');

3
这是SQL注入攻击的经典案例,虽然SO不允许我对答案进行投票,但你不应该使用这种方法来准备SQL语句,而应该使用database/sql https://pkg.go.dev/database/sql。 - Maeve Kennedy
unpack(map[string]string{ "id": "NULL); DROP TABLE tdo.rca_trans_status; --" }) - Qix - MONICA WAS MISTREATED
请使用 https://pkg.go.dev/database/sql#DB.Prepare 创建预处理语句以避免 SQL 注入问题。 - Konrad Kleine

-26

不,语言本身没有直接支持这个功能。Python、Ruby以及你提到的Javascript都是动态/脚本语言。Go更接近于C语言而非任何动态语言。'apply'功能对于动态语言很方便,但对于像C或Go这样的静态语言几乎没有用处。


@user7610,您错了。 您参考的是与问题不同的情况。 在原始问题中,函数声明中没有可变参数。 - user2846569
是的,我想是这样。Python/Ruby所做的可以被视为Go支持的反射调用的语法糖... - user7610

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接