就地更改函数内的切片内容和容量

Change slice content and capacity inside a function in-place

我正在尝试学习围棋,所以这是我非常简单的函数,用于从切片中删除相邻的重复项以供 Donovan 和 Kernighan 的书中练习。
这是代码:https://play.golang.org/p/avHc1ixfck

package main
import "fmt"

func main() {
    a := []int{0, 1, 1, 3, 3, 3}
    removeDup(a)
    fmt.Println(a)
}

func removeDup(s []int) {
    n := len(s)
    tmp := make([]int, 0, n)
    tmp = append(tmp, s[0])
    j := 1
    for i := 1; i < n; i++ {
        if s[i] != s[i-1] {
            tmp = append(tmp, s[i])
            j++
        }
    }
    s = s[:len(tmp)]
    copy(s, tmp)
}

它应该打印出 [0 1 3] - 我检查过,实际上 tmp 在函数的末尾它具有所需的形式。然而,结果是[0 1 3 3 3 3]。我猜有些东西具有 copy 功能。

我能否以某种方式将输入切片 s 替换为 temp 或 trim 到所需的长度?

选项 1

Return @zerkms 建议的新切片。
https://play.golang.org/p/uGJiD3WApS

package main
import "fmt"

func main() {
    a := []int{0, 1, 1, 3, 3, 3}
    a = removeDup(a)
    fmt.Println(a)
}

func removeDup(s []int) []int {
    n := len(s)
    tmp := make([]int, 0, n)
    tmp = append(tmp, s[0])
    for i := 1; i < n; i++ {
        if s[i] != s[i-1] {
            tmp = append(tmp, s[i])
        }
    }
    return tmp
}

选项 2
使用指针传递引用。
与选项 1 的效果相同。

https://play.golang.org/p/80bE5Qkuuj

package main

import "fmt"

func main() {
    a := []int{0, 1, 1, 3, 3, 3}
    removeDup(&a)
    fmt.Println(a)
}

func removeDup(sp *[]int) {
    s := *sp
    n := len(s)
    tmp := make([]int, 0, n)
    tmp = append(tmp, s[0])
    for i := 1; i < n; i++ {
        if s[i] != s[i-1] {
            tmp = append(tmp, s[i])
        }
    }
    *sp = tmp
}

另请参考以下SO线程: Does Go have no real way to shrink a slice? Is that an issue?

这里有两种稍微不同的方法来使用集合和命名类型来实现您想要的效果。命名类型的妙处在于,您可以围绕它们创建接口,并有助于提高大量代码的可读性。

package main

import "fmt"

func main() {
    // returning a list
    a := []int{0, 1, 1, 3, 3, 3}
    clean := removeDup(a)
    fmt.Println(clean)
    // creating and using a named type
    nA := &newArrType{0, 1, 1, 3, 3, 3}
    nA.removeDup2()
    fmt.Println(nA)

    // or... casting your orginal array to the named type
    nB := newArrType(a)
    nB.removeDup2()
    fmt.Println(nB)
}

// using a set
// order is not kept, but a set is returned
func removeDup(s []int) (newArr []int) {
    set := make(map[int]struct{})
    for _, n := range s {
        set[n] = struct{}{}
    }
    newArr = make([]int, 0, len(set))
    for k := range set {
        newArr = append(newArr, k)
    }
    return
}

// using named a typed
type newArrType []int

func (a *newArrType) removeDup2() {
    x := *a
    for i := range x {
        f := i + 1
        if f < len(x) {
            if x[i] == x[f] {
                x = x[:f+copy(x[f:], x[f+1:])]
            }
        }
    }
    // check the last 2 indexes
    if x[len(x)-2] == x[len(x)-1] {
        x = x[:len(x)-1+copy(x[len(x)-1:], x[len(x)-1+1:])]
    }
    *a = x
}