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可以支持函数式编程风格。看见但它不是一种函数式编程语言+我感谢你把这一点说清楚。