如何在for循环中修改切片?

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

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
}

  1. myArticleList = unsubscribe(myArticleList, "http://planet.python.org/rss20.xml")
  2. 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.

  1. type Article struct {
  2. FeedURL string
  3. URL string // should be unique
  4. // ... more data
  5. }
  6. func unsubscribe(articleList []Article, url string) []Article {
  7. // how do I remove every Article from articleList that contains url?
  8. }
  9. func main() {
  10. myArticleList := []Article{
  11. Article{"http://blog.golang.org/feed.atom", "http://blog.golang.org/race-detector"},
  12. Article{"http://planet.python.org/rss20.xml", "http://archlinux.me/dusty/2013/06/29/creating-an-application-in-kivy-part-3/"},
  13. Article{"http://planet.python.org/rss20.xml", "http://feedproxy.google.com/~r/cubicweborg/~3/BncbP-ap0n0/2957378"},
  14. // ... much more examples
  15. }
  16. myArticleList = unsubscribe(myArticleList, "http://planet.python.org/rss20.xml")
  17. fmt.Printf("%+v", myArticleList)
  18. }

What is the efficient way of solving this problem?

At first my code looked like this for unsubscribe:

  1. func unsubscribe(articleList []Article, url string) []Article {
  2. for _, article := range articleList {
  3. if article.FeedURL == url {
  4. articleList = append(articleList[:i], articleList[i+1:]...)
  5. }
  6. }
  7. return articleList
  8. }

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的顺序不重要,使用无序算法;它减少了指针的移动。否则,使用有序算法。无论如何,都要尽量减少指针的移动。
  • 不要在列表末尾留下悬空指针。垃圾收集器会认为它们仍在使用;它查看的是切片的容量而不是切片的长度。
  • 尽量减少内存分配。

例如,

  1. package main
  2. import "fmt"
  3. type Article struct {
  4. FeedURL string
  5. URL string // 应该是唯一的
  6. // ... 更多数据
  7. }
  8. // 从articleList中删除包含url的每个Article,不保留顺序。
  9. func unsubscribeUnordered(a []*Article, url string) []*Article {
  10. for i := 0; i < len(a); i++ {
  11. if a[i].FeedURL == url {
  12. a[len(a)-1], a[i], a = nil, a[len(a)-1], a[:len(a)-1]
  13. i--
  14. }
  15. }
  16. return a
  17. }
  18. // 从articleList中删除包含url的每个Article,保留顺序。
  19. func unsubscribeOrdered(a []*Article, url string) []*Article {
  20. j := 0
  21. for i := 0; i < len(a); i++ {
  22. if a[i].FeedURL == url {
  23. continue
  24. }
  25. if i != j {
  26. a[j] = a[i]
  27. }
  28. j++
  29. }
  30. for k := j; k < len(a); k++ {
  31. a[k] = nil
  32. }
  33. return a[:j]
  34. }
  35. func NewArticleList() []*Article {
  36. return []*Article{
  37. &Article{"http://blog.golang.org/feed.atom", "http://blog.golang.org/race-detector"},
  38. &Article{"http://planet.python.org/rss20.xml", "http://archlinux.me/dusty/2013/06/29/creating-an-application-in-kivy-part-3/"},
  39. &Article{"http://planet.python.org/rss20.xml", "http://feedproxy.google.com/~r/cubicweborg/~3/BncbP-ap0n0/2957378"},
  40. // ... 更多示例
  41. }
  42. }
  43. func PrintArticleList(a []*Article) {
  44. fmt.Print("[")
  45. for _, e := range a {
  46. fmt.Printf("%+v", *e)
  47. }
  48. fmt.Println("]")
  49. }
  50. func main() {
  51. PrintArticleList(NewArticleList())
  52. ao := unsubscribeOrdered(NewArticleList(), "http://planet.python.org/rss20.xml")
  53. PrintArticleList(ao)
  54. auo := unsubscribeUnordered(NewArticleList(), "http://planet.python.org/rss20.xml")
  55. PrintArticleList(auo)
  56. }

输出:

  1. [{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}]
  2. [{FeedURL:http://blog.golang.org/feed.atom URL:http://blog.golang.org/race-detector}]
  3. [{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,

  1. package main
  2. import &quot;fmt&quot;
  3. type Article struct {
  4. FeedURL string
  5. URL string // should be unique
  6. // ... more data
  7. }
  8. // Remove every Article from an articleList that contains url without preserving order.
  9. func unsubscribeUnordered(a []*Article, url string) []*Article {
  10. for i := 0; i &lt; len(a); i++ {
  11. if a[i].FeedURL == url {
  12. a[len(a)-1], a[i], a = nil, a[len(a)-1], a[:len(a)-1]
  13. i--
  14. }
  15. }
  16. return a
  17. }
  18. // Remove every Article from an articleList that contains url while preserving order.
  19. func unsubscribeOrdered(a []*Article, url string) []*Article {
  20. j := 0
  21. for i := 0; i &lt; len(a); i++ {
  22. if a[i].FeedURL == url {
  23. continue
  24. }
  25. if i != j {
  26. a[j] = a[i]
  27. }
  28. j++
  29. }
  30. for k := j; k &lt; len(a); k++ {
  31. a[k] = nil
  32. }
  33. return a[:j]
  34. }
  35. func NewArticleList() []*Article {
  36. return []*Article{
  37. &amp;Article{&quot;http://blog.golang.org/feed.atom&quot;, &quot;http://blog.golang.org/race-detector&quot;},
  38. &amp;Article{&quot;http://planet.python.org/rss20.xml&quot;, &quot;http://archlinux.me/dusty/2013/06/29/creating-an-application-in-kivy-part-3/&quot;},
  39. &amp;Article{&quot;http://planet.python.org/rss20.xml&quot;, &quot;http://feedproxy.google.com/~r/cubicweborg/~3/BncbP-ap0n0/2957378&quot;},
  40. // ... much more examples
  41. }
  42. }
  43. func PrintArticleList(a []*Article) {
  44. fmt.Print(&quot;[&quot;)
  45. for _, e := range a {
  46. fmt.Printf(&quot;%+v&quot;, *e)
  47. }
  48. fmt.Println(&quot;]&quot;)
  49. }
  50. func main() {
  51. PrintArticleList(NewArticleList())
  52. ao := unsubscribeOrdered(NewArticleList(), &quot;http://planet.python.org/rss20.xml&quot;)
  53. PrintArticleList(ao)
  54. auo := unsubscribeUnordered(NewArticleList(), &quot;http://planet.python.org/rss20.xml&quot;)
  55. PrintArticleList(auo)
  56. }

Output:

  1. [{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}]
  2. [{FeedURL:http://blog.golang.org/feed.atom URL:http://blog.golang.org/race-detector}]
  3. [{FeedURL:http://blog.golang.org/feed.atom URL:http://blog.golang.org/race-detector}]

答案2

得分: 0

PeterSO的答案完成了工作,并且效率很高。

但是,我可能会选择像这样简单的方法

  1. func unsubscribe(articleList []Article, url string) (filtered []Article) {
  2. filtered = articleList[:0] // 可选的。重用已分配的内存。
  3. for _, article := range articleList {
  4. if article.FeedURL != url {
  5. filtered = append(filtered, article)
  6. }
  7. }
  8. return
  9. }

只需要大约两秒钟就可以阅读和理解。

这个想法对于文章的指针也适用,就像PeterSO说的那样,如果你的Article结构体很大,这可能是一个好主意。

英文:

PeterSO's answer is gets the job done, and with efficiency.

But, I might go with something simple like this

  1. func unsubscribe(articleList []Article, url string) (filtered []Article) {
  2. filtered = articleList[:0] // optional. reuses already-allocated memory.
  3. for _, article := range articleList {
  4. if article.FeedURL != url {
  5. filtered = append(filtered, article)
  6. }
  7. }
  8. return
  9. }

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.

huangapple
  • 本文由 发表于 2013年6月30日 06:36:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/17385342.html
匿名

发表评论

匿名网友

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

确定