英文:
How to modify a slice within for loop?
问题
type Article struct {
FeedURL string
URL string // should be unique
// ... more data
}
func unsubscribe(articleList []Article, url string) []Article {
var result []Article
for _, article := range articleList {
if article.FeedURL != url {
result = append(result, article)
}
}
return result
}
func main() {
myArticleList := []Article{
Article{"http://blog.golang.org/feed.atom", "http://blog.golang.org/race-detector"},
Article{"http://planet.python.org/rss20.xml", "http://archlinux.me/dusty/2013/06/29/creating-an-application-in-kivy-part-3/"},
Article{"http://planet.python.org/rss20.xml", "http://feedproxy.google.com/~r/cubicweborg/~3/BncbP-ap0n0/2957378"},
// ... much more examples
}
myArticleList = unsubscribe(myArticleList, "http://planet.python.org/rss20.xml")
fmt.Printf("%+v", myArticleList)
}
英文:
I've got a slice of articles in my reading list. Each Article has the attribute "FeedURL" that has the URL of the feed the article came from. When I unsubscribe from a feed, I want to be able to remove every Article that contains that Feed's URL.
type Article struct {
FeedURL string
URL string // should be unique
// ... more data
}
func unsubscribe(articleList []Article, url string) []Article {
// how do I remove every Article from articleList that contains url?
}
func main() {
myArticleList := []Article{
Article{"http://blog.golang.org/feed.atom", "http://blog.golang.org/race-detector"},
Article{"http://planet.python.org/rss20.xml", "http://archlinux.me/dusty/2013/06/29/creating-an-application-in-kivy-part-3/"},
Article{"http://planet.python.org/rss20.xml", "http://feedproxy.google.com/~r/cubicweborg/~3/BncbP-ap0n0/2957378"},
// ... much more examples
}
myArticleList = unsubscribe(myArticleList, "http://planet.python.org/rss20.xml")
fmt.Printf("%+v", myArticleList)
}
What is the efficient way of solving this problem?
At first my code looked like this for unsubscribe:
func unsubscribe(articleList []Article, url string) []Article {
for _, article := range articleList {
if article.FeedURL == url {
articleList = append(articleList[:i], articleList[i+1:]...)
}
}
return articleList
}
But then I realized that this would change the slice and make the for loop unpredictable.
What is an efficient and pretty way to accomplish this?
答案1
得分: 5
为了提高效率:
- 使用指向Article的指针切片,这样我们将移动指针而不是结构体值。
- 如果列表中Article的顺序不重要,使用无序算法;它减少了指针的移动。否则,使用有序算法。无论如何,都要尽量减少指针的移动。
- 不要在列表末尾留下悬空指针。垃圾收集器会认为它们仍在使用;它查看的是切片的容量而不是切片的长度。
- 尽量减少内存分配。
例如,
package main
import "fmt"
type Article struct {
FeedURL string
URL string // 应该是唯一的
// ... 更多数据
}
// 从articleList中删除包含url的每个Article,不保留顺序。
func unsubscribeUnordered(a []*Article, url string) []*Article {
for i := 0; i < len(a); i++ {
if a[i].FeedURL == url {
a[len(a)-1], a[i], a = nil, a[len(a)-1], a[:len(a)-1]
i--
}
}
return a
}
// 从articleList中删除包含url的每个Article,保留顺序。
func unsubscribeOrdered(a []*Article, url string) []*Article {
j := 0
for i := 0; i < len(a); i++ {
if a[i].FeedURL == url {
continue
}
if i != j {
a[j] = a[i]
}
j++
}
for k := j; k < len(a); k++ {
a[k] = nil
}
return a[:j]
}
func NewArticleList() []*Article {
return []*Article{
&Article{"http://blog.golang.org/feed.atom", "http://blog.golang.org/race-detector"},
&Article{"http://planet.python.org/rss20.xml", "http://archlinux.me/dusty/2013/06/29/creating-an-application-in-kivy-part-3/"},
&Article{"http://planet.python.org/rss20.xml", "http://feedproxy.google.com/~r/cubicweborg/~3/BncbP-ap0n0/2957378"},
// ... 更多示例
}
}
func PrintArticleList(a []*Article) {
fmt.Print("[")
for _, e := range a {
fmt.Printf("%+v", *e)
}
fmt.Println("]")
}
func main() {
PrintArticleList(NewArticleList())
ao := unsubscribeOrdered(NewArticleList(), "http://planet.python.org/rss20.xml")
PrintArticleList(ao)
auo := unsubscribeUnordered(NewArticleList(), "http://planet.python.org/rss20.xml")
PrintArticleList(auo)
}
输出:
[{FeedURL:http://blog.golang.org/feed.atom URL:http://blog.golang.org/race-detector}{FeedURL:http://planet.python.org/rss20.xml URL:http://archlinux.me/dusty/2013/06/29/creating-an-application-in-kivy-part-3/}{FeedURL:http://planet.python.org/rss20.xml URL:http://feedproxy.google.com/~r/cubicweborg/~3/BncbP-ap0n0/2957378}]
[{FeedURL:http://blog.golang.org/feed.atom URL:http://blog.golang.org/race-detector}]
[{FeedURL:http://blog.golang.org/feed.atom URL:http://blog.golang.org/race-detector}]
英文:
To be efficient:
- Use a slice of pointers to Articles, then we will be moving pointers
to structures instead of structure values. - If the order of the Articles in the list is not important, use the
unordered algorithm; it reduces pointer movement. Otherwise, use the
ordered algorithm. In any case, minimize pointer movement. - Don't leave dangling pointers at the end of the list. The garbage
collector will think they are still in use; it looks at the slice
capacity not the slice length. - Minimize memory allocations.
For example,
package main
import "fmt"
type Article struct {
FeedURL string
URL string // should be unique
// ... more data
}
// Remove every Article from an articleList that contains url without preserving order.
func unsubscribeUnordered(a []*Article, url string) []*Article {
for i := 0; i < len(a); i++ {
if a[i].FeedURL == url {
a[len(a)-1], a[i], a = nil, a[len(a)-1], a[:len(a)-1]
i--
}
}
return a
}
// Remove every Article from an articleList that contains url while preserving order.
func unsubscribeOrdered(a []*Article, url string) []*Article {
j := 0
for i := 0; i < len(a); i++ {
if a[i].FeedURL == url {
continue
}
if i != j {
a[j] = a[i]
}
j++
}
for k := j; k < len(a); k++ {
a[k] = nil
}
return a[:j]
}
func NewArticleList() []*Article {
return []*Article{
&Article{"http://blog.golang.org/feed.atom", "http://blog.golang.org/race-detector"},
&Article{"http://planet.python.org/rss20.xml", "http://archlinux.me/dusty/2013/06/29/creating-an-application-in-kivy-part-3/"},
&Article{"http://planet.python.org/rss20.xml", "http://feedproxy.google.com/~r/cubicweborg/~3/BncbP-ap0n0/2957378"},
// ... much more examples
}
}
func PrintArticleList(a []*Article) {
fmt.Print("[")
for _, e := range a {
fmt.Printf("%+v", *e)
}
fmt.Println("]")
}
func main() {
PrintArticleList(NewArticleList())
ao := unsubscribeOrdered(NewArticleList(), "http://planet.python.org/rss20.xml")
PrintArticleList(ao)
auo := unsubscribeUnordered(NewArticleList(), "http://planet.python.org/rss20.xml")
PrintArticleList(auo)
}
Output:
[{FeedURL:http://blog.golang.org/feed.atom URL:http://blog.golang.org/race-detector}{FeedURL:http://planet.python.org/rss20.xml URL:http://archlinux.me/dusty/2013/06/29/creating-an-application-in-kivy-part-3/}{FeedURL:http://planet.python.org/rss20.xml URL:http://feedproxy.google.com/~r/cubicweborg/~3/BncbP-ap0n0/2957378}]
[{FeedURL:http://blog.golang.org/feed.atom URL:http://blog.golang.org/race-detector}]
[{FeedURL:http://blog.golang.org/feed.atom URL:http://blog.golang.org/race-detector}]
答案2
得分: 0
PeterSO的答案完成了工作,并且效率很高。
但是,我可能会选择像这样简单的方法
func unsubscribe(articleList []Article, url string) (filtered []Article) {
filtered = articleList[:0] // 可选的。重用已分配的内存。
for _, article := range articleList {
if article.FeedURL != url {
filtered = append(filtered, article)
}
}
return
}
只需要大约两秒钟就可以阅读和理解。
这个想法对于文章的指针也适用,就像PeterSO说的那样,如果你的Article结构体很大,这可能是一个好主意。
英文:
PeterSO's answer is gets the job done, and with efficiency.
But, I might go with something simple like this
func unsubscribe(articleList []Article, url string) (filtered []Article) {
filtered = articleList[:0] // optional. reuses already-allocated memory.
for _, article := range articleList {
if article.FeedURL != url {
filtered = append(filtered, article)
}
}
return
}
which only takes about a two seconds to read, and comprehend.
The idea works fine with pointers to articles too and, like PeterSO said, if your Article struct is big, that may be a good thing to do.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论