“map”和“reduce”可以在Go语言中使用泛型实现吗?

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

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的值。你还指定了两个约束(类型为IO的接口),都设置为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
}

huangapple
  • 本文由 发表于 2022年6月15日 22:34:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/72633293.html
匿名

发表评论

匿名网友

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

确定