dongsiju1941
dongsiju1941
2017-07-23 20:14

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

  • slice
已采纳

I am trying to learn Go, so here is my very simple function for removing adjacent duplicates from slice for exercise from the book by Donovan & Kernighan.
Here is the code: 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)
}

It should print out [0 1 3] - and I checked, actually tmp at the end of the function it has desired form. However, the result is [0 1 3 3 3 3]. I guess there is something with copy function.

Can I somehow replace input slice s with the temp or trim it to desired length?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

2条回答

  • doumei1772 doumei1772 4年前

    Option 1

    Return a new slice as suggested by @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
    }
    

    Option 2
    Use pointers for pass-by-reference.
    The same thing in effect as that of option1.

    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
    }
    

    Also, refer to following SO thread: Does Go have no real way to shrink a slice? Is that an issue?

    点赞 评论 复制链接分享
  • dpv46227 dpv46227 4年前

    Here's two more slightly different ways to achieve what you want using sets and named types. The cool thing about named types is that you can create interfaces around them and can help with the readability of lots of code.

    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
    }
    
    点赞 评论 复制链接分享