英文:
Can 'map' and 'reduce' be implemented in Go with generics
问题
我决定既然泛型已经引入到Go语言中,那么类似于map/reduce
的操作应该是可能的。所以,我尝试了一个天真的方法,但是我得到了错误信息:
./prog.go:18:36: cannot use thing (variable of type int) as type I in argument to mapper
这并没有解释问题是基本的还是我在语法上做错了什么。在Go语言中是否可以实现泛型的map/reduce操作?
package main
import "fmt"
func main() {
things := []int{1, 2, 3, 4}
results := Map(things, func(t int) int {
return t + 1
})
fmt.Printf("%v", results)
}
func Map[I interface{}, O interface{}](things []I, mapper func(thing I) O) []O {
results := make([]O, 0, len(things))
for thing := range things {
results = append(results, mapper(thing))
}
return results
}
英文:
I decided that now that generics have been introduced into Go that something like map/reduce
should be possible. So, I took a naive stab at it and I get the error:
./prog.go:18:36: cannot use thing (variable of type int) as type I in argument to mapper
Which doesn't explain if the problem is fundamental or I am simply doing something wrong syntactically. Can generic map/reduce be implemented in Go?
package main
import "fmt"
func main() {
things := []int{1, 2, 3, 4}
results := Map(things, func(t int) int {
return t + 1
})
fmt.Printf("%v", results)
}
func Map[I interface{}, O interface{}](things []I, mapper func(thing I) O) []O {
results := make([]O, 0, len(things))
for thing := range things {
results = append(results, mapper(thing))
}
return results
}
</details>
# 答案1
**得分**: 2
你对`range`的使用有误。从`range`中提取的单个变量将是索引(类型为`int`),而不是值(类型为`I`,在这种情况下只是巧合地是`int`类型)。
尝试使用以下代码:
```go
for _, thing := range things{...}
英文:
You have incorrect use of range
. A single variable extracted from range
will be the index (type int
), not the value (type I
, which is only coincidentally int
in this case).
Try
for _, thing := range things{...}
答案2
得分: 2
这可以很容易地完成。不过你的代码有一个错误,就在这里:
for thing := range things {
你正在迭代索引值(int),而不是类型为I
的值。你还指定了两个约束(类型为I
和O
的接口),都设置为interface{}
。你可以使用any
代替(它是interface{}
的简写)。
所以只需写成:
func Map[T any, O any](things []T, mapper func(thing T) O) []O {
result := make([]O, 0, len(things))
for _, thing := range things {
result = append(result, mapper(thing))
}
return result
}
演示
这与我在codereview交流中审查的一些代码非常相关,在这里。在查看了代码并写了很多建议的片段后,我决定创建一个包并将其上传到GitHub上。你可以在这里找到该仓库here。
在其中,有一些示例可能会有用,或者帮助你解决一些关于golang泛型的其他问题。我特别考虑了这一部分,你可以使用回调函数过滤泛型映射类型,如下所示:
// 给定 sMap 类型
type sMap[K comparable, V any] struct {
mu *sync.RWMutex
m map[K]V
}
// Filter 返回一个包含与过滤回调参数匹配的元素的映射
func (s *sMap[K, V]) Filter(cb func(K, V) bool) map[K]V {
s.mu.RLock()
defer s.mu.RUnlock()
ret := make(map[K]V, len(s.m))
for k, v := range s.m {
if cb(k, v) {
ret[k] = v
}
}
return ret
}
英文:
This can be done quite easily. You have an error in your code, though right here:
for thing := range things {
You are iterating over the index values (int), not the values of type I
. You're also specifying 2 constraints (types I
and O
) both set to be interface{}
. You can just use any
instead (it's shorthand for interface{}
)
So simply write:
func Map[T any, O any](things []T, mapper func(thing T) O) []O {
result := make([]O, 0, len(things))
for _, thing := range things {
result = append(result, mapper(thing))
}
return result
}
Demo
This is quite closely related to some code I reviewed on codereview exchange here. After going through the code, and writing snippets with a ton of suggestions, I decided to just create a package and throw it up on github instead. You can find the repo here.
In it, there's some examples that may come in handy, or help you work through some other quirks WRT generics in golang. I wsa specifically thinking about this bit, where you can filter a generic map type using callbacks like so:
// given the sMap type
type sMap[K comparable, V any] struct {
mu *sync.RWMutex
m map[K]V
}
// Filter returns a map containing the elements that matched the filter callback argument
func (s *sMap[K, V]) Filter(cb func(K, V) bool) map[K]V {
s.mu.RLock()
defer s.mu.RUnlock()
ret := make(map[K]V, len(s.m))
for k, v := range s.m {
if cb(k, v) {
ret[k] = v
}
}
return ret
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论