英文:
How can I create a first-class map iterator in Go?
问题
我正在编写一个函数,用于迭代映射中的条目。我希望能够像for k, v := range myMap { //...
那样,在迭代时能够干净地处理添加或删除的条目,但是我每次迭代只处理一个键/值对,所以不能使用range。我想要这样的功能:
func processItem(i iterator) bool {
k, v, ok := i.next()
if(!ok) {
return false
}
process(v)
return true
}
var m = make(map[string]widget)
// ...
i := makeIterator(m)
for processItem(i) {
// 可能在这里添加/删除m中的条目的代码
}
我知道range使用了一个在src/runtime/hashmap.go
中定义的hiter
结构和相关函数来执行迭代。有没有办法以一种具体化(first-class)的Go对象的形式访问这个迭代器?
有没有一种替代策略可以很好地处理映射的迭代、插入和删除,并提供一个具体化的迭代器对象?
**附加问题:**有没有一种替代策略可以迭代映射,并且还可以将映射和迭代器序列化到磁盘,然后恢复,继续从上次停止的地方进行迭代?(显然,内置的range
迭代器没有这个功能!)
英文:
I am writing a function that iterates over the entries in a map. I want to be able to deal cleanly with items which are added or deleted from the map while iterating, like for k, v := range myMap { //...
does, but I am processing just one key/value pair per iteration so I can't use range. I want something like:
func processItem(i iterator) bool {
k, v, ok := i.next()
if(!ok) {
return false
}
process(v)
return true
}
var m = make(map[string]widget)
// ...
i := makeIterator(m)
for processItem(i) {
// code which might add/remove item from m here
}
I know that range is using a 'hiter
' struct and associated functions, as defined in src/runtime/hashmap.go
, to perform iteration. Is there some way to gain access to this iterator as a reified (first-class) Go object?
Is there an alternative strategy for iterating over a map which would deal well with insertions/deletions but give a first-class iterator object?
Bonus question: is there an alternative strategy for iterating over a map which could also deal with the map and iterator being serialised to disk and then restored, with iteration continuing from where it left off? (Obviously the built-in range
iterator does not have this capability!)
答案1
得分: 6
你无法
唯一的遍历map
的方法是使用for range
,但你无法从中获取迭代器对象。
英文:
You can't
The only way to iterate over a map
is by using for range
and you can't get an iterator object out of that.
答案2
得分: 4
你可以将通道用作迭代器。
你的迭代器可以是一个返回通道的函数,该通道将当前迭代值传递给接收者:
func iterator(m map[string]widget) chan iteration {
c := make(chan iteration)
go func() {
for k, v := range m {
c <- iteration{k, v}
}
close(c)
}()
return c
}
当然,这并不是通用的。如果你确实需要通用性,可以使用interface{}
和/或反射来实现,但如果你真的需要的话,这应该不难。
在迭代结束时关闭通道将通知迭代的结束,稍后会进行演示。
iteration
类型只是为了让你可以同时发送键和值,它可能如下所示:
type iteration struct {
key string
value widget
}
有了这个,你可以这样做(在playground上):
m := map[string]widget{"foo": widget{3}, "bar": widget{4}}
i := iterator(m)
iter, ok := <-i
fmt.Println(iter, ok)
iter, ok = <-i
fmt.Println(iter, ok)
iter, ok = <-i
fmt.Println(iter, ok)
输出结果为:
{foo {3}} true
{bar {4}} true
{ {0}} false
英文:
You can use channels as iterators.
Your iterator would be a function returning a channel that communicates the current iteration value to whoever receives it:
func iterator(m map[string]widget) chan iteration {
c := make(chan iteration)
go func() {
for k,v := range m {
c <- iteration{k,v}
}
close(c)
}()
return c
}
This is of course not generic, you could make it generic using interface{}
and/or reflection but that shouldn't be too hard if you actually need it.
Closing the channel at the end of iteration will notify the end of iteration, demonstrated later.
The iteration
type is just there so you can send key and value at the same time, it would look something like this:
type iteration struct {
key string
value widget
}
With this you can then do this (on play):
m := map[string]widget{"foo": widget{3}, "bar": widget{4}}
i := iterator(m)
iter, ok := <- i
fmt.Println(iter, ok)
iter, ok = <- i
fmt.Println(iter, ok)
iter, ok = <- i
fmt.Println(iter, ok)
which yields
{foo {3}} true
{bar {4}} true
{ {0}} false
答案3
得分: 4
一个非常简单的方法是获取映射中所有键的列表,并将列表和映射封装在一个迭代器结构中。当我们想要下一个键时,我们从列表中取出尚未从映射中删除的键:
type iterator struct {
m map[string]widget
keys []string
}
func newIterator(m map[string]widget) *iterator {
it := iterator{m, make([]string, len(m))}
i := 0
for k, _ := range m {
it.keys[i] = k
i++
}
return &it
}
func (it *iterator) next() (string, widget, bool) {
for len(it.keys) > 0 {
k := it.keys[0]
it.keys = it.keys[1:]
if _, exists := it.m[k]; exists {
return k, it.m[k], true
}
}
return "", widget{0}, false
}
英文:
A very simple approach is to obtain a list of all the keys in the map, and package the list and the map up in an iterator struct. When we want the next key, we take the next one from the list that hasn't been deleted from the map:
type iterator struct {
m map[string]widget
keys []string
}
func newIterator(m map[string]widget) *iterator {
it := iterator{m, make([]string, len(m))}
i := 0
for k, _ := range m {
it.keys[i] = k
i++
}
return &it
}
func (it *iterator) next() (string, widget, bool) {
for len(it.keys) > 0 {
k := it.keys[0]
it.keys = it.keys[1:]
if _, exists := it.m[k]; exists {
return k, it.m[k], true
}
}
return "", widget{0}, false
}
答案4
得分: 0
你可以定义自己的地图类型。解决并发问题也是很好的:
type ConcurrentMap struct {
sync.RWMutex
items map[string]interface{}
}
type ConcurrentMapItem struct {
Key string
Value interface{}
}
func (cm *ConcurrentMap) Iter() <-chan ConcurrentMapItem {
c := make(chan ConcurrentMapItem)
f := func() {
cm.Lock()
defer cm.Unlock()
for k, v := range cm.items {
c <- ConcurrentMapItem{k, v}
}
close(c)
}
go f()
return c
}
这段代码定义了一个名为ConcurrentMap
的结构体,它包含了一个sync.RWMutex
类型的字段和一个map[string]interface{}
类型的字段。另外还定义了一个名为ConcurrentMapItem
的结构体,它包含了Key
和Value
两个字段。
ConcurrentMap
结构体还定义了一个名为Iter
的方法,该方法返回一个只读通道<-chan ConcurrentMapItem
。在该方法内部,首先创建了一个通道c
,然后定义了一个匿名函数f
。在f
函数内部,通过调用cm.Lock()
和cm.Unlock()
方法来保证并发安全性。然后使用range
循环遍历cm.items
中的键值对,并将每个键值对发送到通道c
中。最后关闭通道c
。最后,通过使用go
关键字在一个新的goroutine中执行f
函数,并返回通道c
。
这样,你就可以使用ConcurrentMap
类型来创建一个并发安全的地图,并通过调用Iter
方法来遍历地图中的键值对。
英文:
You can define your own map type. Also it will be good to solve concurrency problem:
type ConcurrentMap struct {
sync.RWMutex
items map[string]interface{}
}
type ConcurrentMapItem struct {
Key string
Value interface{}
}
func (cm *ConcurrentMap) Iter() <-chan ConcurrentMapItem {
c := make(chan ConcurrentMapItem)
f := func() {
cm.Lock()
defer cm.Unlock()
for k, v := range cm.items {
c <- ConcurrentMapItem{k, v}
}
close(c)
}
go f()
return c
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论