在Golang中删除切片元素的方法是原地删除。

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

In place deletion of Golang slice elements

问题

我想以原地方式从集合中删除元素。考虑以下代码片段:

package main

import "fmt"

type Ints []int

func (xs Ints) Filter() {
    for i := 0; i < len(xs); i++ {
        if xs[i]%2 == 0 { // 或者其他过滤函数
            xs = append(xs[:i], xs[i+1:]...)
        }
        fmt.Printf("i %+v\n", i)
        fmt.Printf("xs %+v\n", xs)
    }
}

func main() {
    a := Ints([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})

    fmt.Printf("初始 a %+v\n", a)

    a.Filter()

    fmt.Printf("最终 a %+v\n", a)
}

这段代码的令人惊讶的结果是:最终 a [1 3 5 7 9 10 10 10 10 10]

我想知道如何解决这个问题。我相当确定接收者需要是指向 Ints 的指针。但这会使代码变得混乱一些(可能需要在每个地方添加 *xs 并加上括号),但更重要的是它产生了相同的结果。

英文:

I want to delete elements from a collection in an in-place manner.
Consider the following snippet:

package main

import &quot;fmt&quot;

type Ints []int

func (xs Ints) Filter() {
    for i := 0; i &lt; len(xs); i++ {
	    if xs[i]%2 == 0 { // Or some other filtering function
		     xs = append(xs[:i], xs[i+1:]...)
	    }
	    fmt.Printf(&quot;i %+v\n&quot;, i)
	    fmt.Printf(&quot;xs %+v\n&quot;, xs)
    }
}

func main() {
    a := Ints([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})

    fmt.Printf(&quot;initial a %+v\n&quot;, a)

    a.Filter()

    fmt.Printf(&quot;final a %+v\n&quot;, a)
}

http://play.golang.org/p/1nL6Il2Gf1

The surprising result is: final a [1 3 5 7 9 10 10 10 10 10]

I wonder how to do this. I'm pretty sure the receiver needs to be a pointer to Ints. But that messes up the code somewhat (adding *xs everywhere possibly with brackets) but more importantly it yields the same result.

答案1

得分: 2

我会通过移动元素、调整切片大小和使用指针来完成。类似这样的代码:

package main

import "fmt"

type Ints []int

func (xs *Ints) Filter() {
    filterPos := 0
    for i := 0; i < len(*xs); i++ {
        if (*xs)[i]%2 == 0 { // 或者其他过滤函数
             (*xs)[filterPos] = (*xs)[i]
             filterPos++
        }
    }
    (*xs) = (*xs)[:filterPos]
}

func main() {
    a := Ints([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})

    fmt.Printf("初始 a %+v\n", a)

    a.Filter()

    fmt.Printf("最终 a %+v\n", a)
}

请注意,这是一个示例代码,用于演示如何通过移动元素、调整切片大小和使用指针来过滤切片。你可以根据自己的需求进行修改和适应。

英文:

I'd do it by moving elements, then resizing the slice, and using a pointer. Something like this:

package main

import &quot;fmt&quot;

type Ints []int

func (xs *Ints) Filter() {
    filterPos := 0
    for i := 0; i &lt; len(*xs); i++ {
        if (*xs)[i]%2 == 0 { // Or some other filtering function
             (*xs)[filterPos] = (*xs)[i]
	     filterPos++
        }
    }
    (*xs) = (*xs)[:filterPos]
}

func main() {
    a := Ints([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})

    fmt.Printf(&quot;initial a %+v\n&quot;, a)

    a.Filter()

    fmt.Printf(&quot;final a %+v\n&quot;, a)
}

答案2

得分: 1

你必须在这里使用指针。如果你不喜欢在每个地方都添加 *xs,只需使用一个临时变量来执行所有操作,然后将其设置回去。这是代码的链接:http://play.golang.org/p/eAFkV3Lwh6

英文:

You must use pointer here. If you don't like adding *xs every where just use a temp variable to do all operations then set it back. Here is the code http://play.golang.org/p/eAFkV3Lwh6

答案3

得分: 1

你的代码几乎是正确的。

第一个 bug 是在删除元素时需要避免执行 i++,否则 i++ 会跳过下一个未读取的元素。这就是为什么我将其放在 else 子句中的原因。

第二个 bug 是 xsFilter 函数的局部变量,所以如果你改变它所指向的内容(使用 xs = ...),这不会改变 main 函数中 a 所指向的内容。你可以通过将其声明为指针(像其他人发布的那样使用 *xs)或者通过返回新的过滤切片来解决这个问题,就像我下面所做的那样。

package main

import "fmt"

type Ints []int

func (xs Ints) Filtered() Ints {
	for i := 0; i < len(xs); {
		if xs[i]%2 == 0 {
			xs = append(xs[:i], xs[i+1:]...)
		} else {
			i++
		}
	}
	return xs
}

func main() {
	a := Ints([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
	b := a.Filtered()
	fmt.Println(b)
}

你可以在这里运行代码:http://play.golang.org/p/Nre7w4KQ78

英文:

Your code was almost right.

The first bug is that you need to avoid doing i++ when you're deleting an element, otherwise the i++ skips over the next, unread element. That's why I've put it in the else clause.

The second bug is that xs is a local variable of the Filter function, so if you change what it points to (with xs = ...) that does not change what a points to inside main. You can solve this issue by making it a pointer (*xs as others have posted) or by returning the new filtered slice, as I've done below.

package main

import &quot;fmt&quot;

type Ints []int

func (xs Ints) Filtered() Ints {
	for i := 0; i &lt; len(xs); {
		if xs[i]%2 == 0 {
			xs = append(xs[:i], xs[i+1:]...)
		} else {
			i++
		}
	}
	return xs
}

func main() {
	a := Ints([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
	b := a.Filtered()
	fmt.Println(b)
}

http://play.golang.org/p/Nre7w4KQ78

huangapple
  • 本文由 发表于 2014年7月14日 17:33:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/24733466.html
匿名

发表评论

匿名网友

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

确定