go递归函数如何传递数组切片slice?

数组切片slice这个东西看起来很美好,真正用起来会发现有诸多的不爽。

第一,数组、数组切片混淆不清,使用方式完全一样,有时候一些特性又完全不一样,搞不清原理很容易误使用。

第二,数组切片的append操作,每次对slice append操作,都返回一个新的slice的引用,对slice的引用没法保持,这样在函数传递slice的情况下append,在调用函数的上下文中看不到slice append的效果。如果想要这种方式凑效,不得不另辟蹊径。本文主要说一下如何解决这个窘境的方法。

函数传递slice存在什么问题?

func sliceModify(slice []int) {
    // slice[0] = 88
    slice = append(slice, 6)
}
func main() {
    slice := []int{1, 2, 3, 4, 5}
    sliceModify(slice)
    fmt.Println(slice)
}

输出:

[1 2 3 4 5]

问题所在:

虽然说数组切片在函数传递时是按照引用的语义传递的,比如说在sliceModify函数里面slice[0] = 88,在方法调用的上下文中,调用函数对slice引用的改表是看得见的。

但是在对slice进行append操作的时候,我们惊奇的发现,这次又不管用了。原因就是append操作会返回这个扩展了的slice的引用,必须让原引用重新赋值为新slice的引用,说白了就是,传递过来的这个指针原来指了内存中的A区域,A区域是原数组的真正所在。经过一次 append之后,要把这个指针改为指向B,B对应append后新的slice的引用。但是方法调用的上下文里的slice指针还是指向了老的A内存区域。

这个逻辑实在有些奇葩,这里我不得不再次吐槽append的设计。有人说这个问题好解决啊,只需要在sliceModify函数的返回值中把append后新的slice引用返回就好了。这样做当然是可以滴,但是像递归调用的函数就不好解决了。

下面就说一下这个问题的解决办法,方法也很简单,就是传递指针的指针。虽然有些绕,但是总算把问题解决了。当然也有其他的办法,比如按照java等语言的方式,自己实现一个ArrayList,在对可变数组扩展的时候,千万表改变引用了。

func sliceModify(slice *[]int) {
    *slice = append(*slice, 6)
}
func main() {
    slice := []int{1, 2, 3, 4, 5}
    sliceModify(&slice)
    fmt.Println(slice)
}

这次就可以输出预期的结果了:

[1 2 3 4 5 6]

递归调用的例子:

func insertTo10(arr *[]int) {
    length := len(*arr)
    if length == 10 {
        return
    }
    *arr = append(*arr, length)
    insertTo10(arr)
}
func main() {
    arr10 := []int{}
    insertTo10(&arr10)
    fmt.Println(arr10)
}

还可以访问我树莓派上搭的博客地址:

http://www.codeforfun.info/