英文:
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 "fmt"
type Ints []int
func (xs Ints) Filter() {
    for i := 0; i < len(xs); i++ {
	    if xs[i]%2 == 0 { // Or some other filtering function
		     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("initial a %+v\n", a)
    a.Filter()
    fmt.Printf("final a %+v\n", 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 "fmt"
type Ints []int
func (xs *Ints) Filter() {
    filterPos := 0
    for i := 0; i < 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("initial a %+v\n", a)
    a.Filter()
    fmt.Printf("final a %+v\n", 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 是 xs 是 Filter 函数的局部变量,所以如果你改变它所指向的内容(使用 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 "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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论