Go 将切片作为函数参数传递,并修改原始切片
我知道Go中的所有内容都是通过值传递的,这意味着如果我给函数一个切片,并且该函数使用内置的Go 将切片作为函数参数传递,并修改原始切片,go,Go,我知道Go中的所有内容都是通过值传递的,这意味着如果我给函数一个切片,并且该函数使用内置的append函数附加到切片,那么原始切片将不会具有附加到函数范围中的值 例如: nums := []int{1, 2, 3} func addToNumbs(nums []int) []int { nums = append(nums, 4) fmt.Println(nums) // []int{1, 2, 3, 4} } fmt.Println(nums) // []int{1, 2,
append
函数附加到切片,那么原始切片将不会具有附加到函数范围中的值
例如:
nums := []int{1, 2, 3}
func addToNumbs(nums []int) []int {
nums = append(nums, 4)
fmt.Println(nums) // []int{1, 2, 3, 4}
}
fmt.Println(nums) // []int{1, 2, 3}
这给我带来了一个问题,因为我试图在一个累积的片上进行递归,基本上是一个reduce
类型的函数,reducer调用自己除外
以下是一个例子:
func Validate(obj Validatable) ([]ValidationMessage, error) {
messages := make([]ValidationMessage, 0)
if err := validate(obj, messages); err != nil {
return messages, err
}
return messages, nil
}
func validate(obj Validatable, accumulator []ValidationMessage) error {
// If something is true, recurse
if something {
if err := validate(obj, accumulator); err != nil {
return err
}
}
// Append to the accumulator passed in
accumulator = append(accumulator, message)
return nil
}
上面的代码给出了与第一个示例相同的错误,累加器
没有获得所有附加值,因为它们只存在于函数的范围内
为了解决这个问题,我将指针结构传递到函数中,该结构包含累加器。这个解决方案很有效
我的问题是,有没有更好的方法来做到这一点,我的方法是惯用的吗
更新的解决方案(感谢icza):
我只返回递归函数中的切片。这样一个掌门人,应该想到这一点
func Validate(obj Validatable) ([]ValidationMessage, error) {
messages := make([]ValidationMessage, 0)
return validate(obj, messages)
}
func validate(obj Validatable, messages []ValidationMessage) ([]ValidationMessage, error) {
err := v.Struct(obj)
if _, ok := err.(*validator.InvalidValidationError); ok {
return []ValidationMessage{}, errors.New(err.Error())
}
if _, ok := err.(validator.ValidationErrors); ok {
messageMap := obj.Validate()
for _, err := range err.(validator.ValidationErrors) {
f := err.StructField()
t := err.Tag()
if v, ok := err.Value().(Validatable); ok {
return validate(v, messages)
} else if _, ok := messageMap[f]; ok {
if _, ok := messageMap[f][t]; ok {
messages = append(messages, ValidationMessage(messageMap[f][t]))
}
}
}
}
return messages, nil
}
如果切片的当前大小不足以追加新值从而更改基础数组,则切片将根据需要动态增长。如果未返回此新切片,您的追加更改将不可见 例如:
package main
import (
"fmt"
)
func noReturn(a []int, data ...int) {
a = append(a, data...)
}
func returnS(a []int, data ...int) []int {
return append(a, data...)
}
func main() {
a := make([]int, 1)
noReturn(a, 1, 2, 3)
fmt.Println(a) // append changes will not visible since slice size grew on demand changing underlying array
a = returnS(a, 1, 2, 3)
fmt.Println(a) // append changes will be visible here since your are returning the new updated slice
}
结果:
[0]
[0 1 2 3]
注:
将数据追加到切片时,如果切片的底层数组没有足够的空间,将分配一个新数组。然后,旧数组中的元素将被复制到这个新内存中,同时在后面添加新数据。如果要将一个切片作为参数传递给函数,并让该函数修改原始切片,则必须传递指向该切片的指针:
func myAppend(list *[]string, value string) {
*list = append(*list, value)
}
我不知道围棋编译器在这方面是幼稚还是聪明;性能留作注释部分的练习。您传递的切片是对数组的引用,这意味着大小是固定的。如果您只是修改了存储的值,那么该值将在调用的函数外部更新 但是,如果您将新元素添加到切片中,它将重新切片以容纳新元素,换句话说,将创建一个新切片,并且不会覆盖旧切片
作为总结,如果需要扩展或剪切切片,请将指针传递给切片。否则,使用切片本身就足够了。返回新切片,就像内置的
append()
那样(并在调用方重新分配返回值),或传递指向切片的指针,不是切片头。我不能返回新的切片,因为我必须处理该切片以进行递归,但我认为将指针传递给slight可能会有效吗?让我试试..递归调用也可以返回值,我看不出有什么问题。如果您使用传递切片指针,请注意,处理它们更“冗长”,请参见示例。您知道吗,您完全正确。不知道我在想什么。从递归函数返回切片对我来说效果很好。非常感谢。@OmarIlias通过一个切片头足够快,因为它很小。如果您将指针传递给它,传递可能会更快,但为了使用它,您必须“不断”(每次使用时)取消对它的引用,代码将更难看,因此您将失去优势。试想一下:内置的append()
可以编写为获取一个指针,它不需要返回新的切片(也不需要重新分配结果),但它在另一条路径上需要切片而不是指向它的指针。谢谢,这是一个很好的解释,然而,它并没有提供问题的解决方案:)尽管如此,我已经通过在递归函数中返回切片找到了解决方案,然后我不必尝试操作指向切片的指针或传递包含切片的指针结构。“如果切片已初始化,大小足以附加新值,则不必返回切片。“这是错误的。如果追加到切片,即使基础数组有足够的空间容纳追加的值,追加的值也不会出现在调用函数的切片中,因为切片标头还包含长度和容量字段,并且追加不会更新这些字段。您可以通过超出切片长度的子切片来访问它们,但它们不会自动显示。示例:实际上,它显然比作为全局变量访问数组要快——这解释了为什么追加操作并不总是修改原始传递的切片,即使切片是“引用类型”,因为底层数组的头是指针+1作为解释,尽管在实施/示例方面,本可以使用此评论做得更好