在函数内部原地修改切片的内容和容量。

huangapple go评论82阅读模式
英文:

Change slice content and capacity inside a function in-place

问题

我正在尝试学习Go语言,所以这是我为了完成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函数出了问题。

我能否以某种方式用tmp替换输入切片s,或者将其修剪为所需的长度?

英文:

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 &quot;fmt&quot;

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 &lt; 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?

答案1

得分: 4

选项1

按照 @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
}

另外,请参考以下 Stack Overflow 帖子:
https://stackoverflow.com/questions/16748330/does-go-have-no-real-way-to-shrink-a-slice-is-that-an-issue

英文:

Option 1

Return a new slice as suggested by @zerkms.
https://play.golang.org/p/uGJiD3WApS

package main
import &quot;fmt&quot;

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 &lt; 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 &quot;fmt&quot;

func main() {
	a := []int{0, 1, 1, 3, 3, 3}
	removeDup(&amp;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 &lt; n; i++ {
		if s[i] != s[i-1] {
			tmp = append(tmp, s[i])
		}
	}
	*sp = tmp
}

Also, refer to following SO thread:
https://stackoverflow.com/questions/16748330/does-go-have-no-real-way-to-shrink-a-slice-is-that-an-issue

答案2

得分: 0

以下是你提供的代码的中文翻译:

这里有两种稍微不同的方法来实现你想要的目标,使用集合和命名类型。命名类型的好处是你可以围绕它们创建接口,并且可以提高大量代码的可读性。

package main

import "fmt"

func main() {
	// 返回一个列表
	a := []int{0, 1, 1, 3, 3, 3}
	clean := removeDup(a)
	fmt.Println(clean)
	// 创建并使用命名类型
	nA := &newArrType{0, 1, 1, 3, 3, 3}
	nA.removeDup2()
	fmt.Println(nA)

	// 或者...将原始数组转换为命名类型
	nB := newArrType(a)
	nB.removeDup2()
	fmt.Println(nB)
}

// 使用集合
// 顺序不保留,但返回一个集合
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
}

// 使用命名类型
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:])]
			}
		}
	}
	// 检查最后两个索引
	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
}

希望对你有帮助!

英文:

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 &quot;fmt&quot;
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 := &amp;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 &lt; 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
}

huangapple
  • 本文由 发表于 2017年7月24日 04:14:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/45269506.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定