Go 字符串上的简单mapReduce操作
我有一个字符串列表Go 字符串上的简单mapReduce操作,go,Go,我有一个字符串列表 elems := [n]string{...} 我想执行一个简单的mapReduce操作,以便 将每个字符串映射到不同的字符串,比如说string->$string 使用分隔符将所有字符串缩减为一个字符串,例如{s1,s2,s3}->s1@s2@s3 总而言之:{s1,s2,s3}->$s1@$s2@$s3 最好的方法是什么? 我在寻找效率和可读性 如果它足够通用,不仅可以映射列表,还可以映射字符串,那么除了遍历每个字符串之外,您就没有太多选择了。如果转换ALGO耗时且需要
elems := [n]string{...}
我想执行一个简单的mapReduce操作,以便
string->$string
{s1,s2,s3}->s1@s2@s3
{s1,s2,s3}->$s1@$s2@$s3
最好的方法是什么?
我在寻找效率和可读性
如果它足够通用,不仅可以映射列表,还可以映射字符串,那么除了遍历每个字符串之外,您就没有太多选择了。如果转换ALGO耗时且需要速度,可以考虑拆分作业并使用GO例程。最后,您可以使用strings.Join函数,它有一个指定分隔符的选项,这通常可以有效地执行reduce部分。数据集的大小也是一个考虑因素,对于较大的列表,您可能希望将性能与字符串进行比较。加入您自己的自定义算法,看看您是否希望使用多个go例程/通道来实现所需的功能。对于仅映射列表,除了遍历每个字符串之外,您别无选择。如果转换ALGO耗时且需要速度,可以考虑拆分作业并使用GO例程。最后,您可以使用strings.Join函数,它有一个指定分隔符的选项,这通常可以有效地执行reduce部分。数据集的大小也是一个考虑因素,对于较大的列表,您可能希望将性能与字符串进行比较。加入您自己的自定义算法,看看是否要使用多个go例程/通道来实现您想要的结果。如果您不需要单独完成这两件事,只需使用
strings.Join()
,即可获得最终结果:
打印$a@$b@$c
如果不需要分别完成这两件事,只需使用
字符串即可实现最终结果。Join()
:
打印$a@$b@$c
Go显然不是函数式编程语言 您可以使用for循环进行映射和减少
a := []string{"a", "b", "c"}
result := "initvalue"
for n, i := range a {
result += i + string(n)
}
Go显然不是一种函数式编程语言 您可以使用for循环进行映射和减少
a := []string{"a", "b", "c"}
result := "initvalue"
for n, i := range a {
result += i + string(n)
}
如果您不打算在map函数中执行任何类型的IO操作(意味着它们只执行一些计算),那么使其并发肯定会使其速度变慢,即使您正在执行一些IO,您也应该进行基准测试。并发并不一定会使事情更快,有时还会增加不必要的复杂性。在许多情况下,只要一个简单的for循环就足够了 如果这里的映射函数是IO绑定的,或者正在进行某种计算量很大的计算,而这种计算确实受益于并发,那么解决方案可能会有所不同。例如,可以用于超越一台机器并分配工作负载 这是一个相对简单的示例。减少阶段不是多级阶段,而是阻塞阶段:
import (
"fmt"
"strings"
"sync"
"testing"
"github.com/stretchr/testify/assert"
)
type elem struct {
index int
value interface{}
}
func feed(elems []interface{}) <-chan elem {
result := make(chan elem)
go func() {
for k, v := range elems {
e := elem{
index: k,
value: v,
}
result <- e
}
close(result)
}()
return result
}
func mapf(
input <-chan elem,
mapFunc func(elem) elem) <-chan elem {
result := make(chan elem)
go func() {
for e := range input {
eres := mapFunc(e)
result <- eres
}
close(result)
}()
return result
}
// is blocking
func reducef(
input <-chan elem,
reduceFunc func([]interface{}) interface{}) interface{} {
buffer := make(map[int]interface{})
l := 0
for v := range input {
buffer[v.index] = v.value
if v.index > l {
l = v.index
}
}
data := make([]interface{}, l+1)
for k, v := range buffer {
data[k] = v
}
return reduceFunc(data)
}
func fanOutIn(
elemFeed <-chan elem,
mapFunc func(elem) elem, mapCount int,
reduceFunc func([]interface{}) interface{}) interface{} {
MR := make(chan elem)
wg := &sync.WaitGroup{}
for i := 0; i < mapCount; i++ {
mapResult := mapf(elemFeed, mapFunc)
wg.Add(1)
go func() {
defer wg.Done()
for v := range mapResult {
MR <- v
}
}()
}
go func() {
wg.Wait()
close(MR)
}()
return reducef(MR, reduceFunc)
}
func Test01(t *testing.T) {
elemFeed := feed([]interface{}{1, 2, 3})
finalResult := fanOutIn(
elemFeed,
func(e elem) elem {
return elem{
index: e.index,
value: fmt.Sprintf("[%v]", e.value),
}
},
3,
func(sl []interface{}) interface{} {
strRes := make([]string, len(sl))
for k, v := range sl {
strRes[k] = v.(string)
}
return strings.Join(strRes, ":")
})
assert.Equal(t, "[1]:[2]:[3]", finalResult)
}
导入(
“fmt”
“字符串”
“同步”
“测试”
“github.com/stretchr/authentic/assert”
)
类型元素结构{
索引整数
值接口{}
}
func feed(elems[]interface{})如果您不打算在map函数中执行任何类型的IO操作(意味着它们只是在做一些计算),那么使其并发肯定会使它变慢,即使您正在做一些IO,您也应该进行基准测试。并发并不一定会使事情更快,有时还会增加不必要的复杂性。在许多情况下,只要一个简单的for循环就足够了
如果这里的映射函数是IO绑定的,或者正在进行某种计算量很大的计算,而这种计算确实受益于并发,那么解决方案可能会有所不同。例如,可以用于超越一台机器并分配工作负载
这是一个相对简单的示例。减少阶段不是多级阶段,而是阻塞阶段:
import (
"fmt"
"strings"
"sync"
"testing"
"github.com/stretchr/testify/assert"
)
type elem struct {
index int
value interface{}
}
func feed(elems []interface{}) <-chan elem {
result := make(chan elem)
go func() {
for k, v := range elems {
e := elem{
index: k,
value: v,
}
result <- e
}
close(result)
}()
return result
}
func mapf(
input <-chan elem,
mapFunc func(elem) elem) <-chan elem {
result := make(chan elem)
go func() {
for e := range input {
eres := mapFunc(e)
result <- eres
}
close(result)
}()
return result
}
// is blocking
func reducef(
input <-chan elem,
reduceFunc func([]interface{}) interface{}) interface{} {
buffer := make(map[int]interface{})
l := 0
for v := range input {
buffer[v.index] = v.value
if v.index > l {
l = v.index
}
}
data := make([]interface{}, l+1)
for k, v := range buffer {
data[k] = v
}
return reduceFunc(data)
}
func fanOutIn(
elemFeed <-chan elem,
mapFunc func(elem) elem, mapCount int,
reduceFunc func([]interface{}) interface{}) interface{} {
MR := make(chan elem)
wg := &sync.WaitGroup{}
for i := 0; i < mapCount; i++ {
mapResult := mapf(elemFeed, mapFunc)
wg.Add(1)
go func() {
defer wg.Done()
for v := range mapResult {
MR <- v
}
}()
}
go func() {
wg.Wait()
close(MR)
}()
return reducef(MR, reduceFunc)
}
func Test01(t *testing.T) {
elemFeed := feed([]interface{}{1, 2, 3})
finalResult := fanOutIn(
elemFeed,
func(e elem) elem {
return elem{
index: e.index,
value: fmt.Sprintf("[%v]", e.value),
}
},
3,
func(sl []interface{}) interface{} {
strRes := make([]string, len(sl))
for k, v := range sl {
strRes[k] = v.(string)
}
return strings.Join(strRes, ":")
})
assert.Equal(t, "[1]:[2]:[3]", finalResult)
}
导入(
“fmt”
“字符串”
“同步”
“测试”
“github.com/stretchr/authentic/assert”
)
类型元素结构{
索引整数
值接口{}
}
func feed(elems[]interface{})您需要保留序列吗?@KavehShahbazian yes您需要保留序列吗?@KavehShahbazian yes这太具体了,转换可能比“$”复杂+str这太具体了,转换可能比“$”复杂+strI agree。Go可以支持函数式编程风格。看见但它不是一种函数式编程语言+我同意你的看法。Go可以支持函数式编程风格。看见但它不是一种函数式编程语言+我感谢你把这一点说清楚。