对字符串进行简单的mapReduce操作

时间:2017-07-20 06:35:30

标签: go

我有一个字符串列表

store = {
  componentStore: [[1, 1, 1, 1], [1, 1], [1, 0, 0, 0]]
}

我想执行一个简单的mapReduce操作,这样我

  1. 将每个字符串映射到不同的字符串,让我们说elems := [n]string{...}

  2. 使用分隔符将所有字符串缩减为一个字符串,例如string -> $string

  3. 总而言之:{s1, s2, s3} -> s1@s2@s3

    最好的方法是什么?

    我正在寻找效率和可读性

    奖励积分,如果它的通用性不仅适用于字符串

4 个答案:

答案 0 :(得分:2)

对于仅映射列表,除了遍历每个字符串之外,您没有太多选择。如果变换算法耗时并且您需要速度,则可以考虑拆分作业并使用go例程。最后,您可以使用strings.Join函数,它具有指定分隔符的选项,这通常可以有效地执行reduce部分。数据集的大小也可以作为考虑因素,对于较大的列表,您可能希望将性能与strings.Join和您自己的自定义算法进行比较,看看是否要使用多个go例程/通道来实现您想要的。

答案 1 :(得分:1)

如果您不需要单独执行这两项操作,只需使用strings.Join()即可达到最终结果:

package main

import (
    "fmt"
    "strings"
)

func main() {
    a := []string{"a", "b", "c"}
    p := "$"
    fmt.Println(p + strings.Join(a[:], "@"+p))
}

打印$a@$b@$c

playground

答案 2 :(得分:0)

Go显然不是函数式编程语言。

使用for循环进行映射和缩小。

a := []string{"a", "b", "c"}
result := "initvalue"
for n, i := range a {
  result += i + string(n)
}

答案 3 :(得分:0)

如果您不打算在 map 函数中执行任何类型的IO操作(意味着它们只进行一些计算),那么使它并发会使它变得更慢,即使你是做一些IO,你应该做基准测试。并发性不会使事情变得更快,有时会增加不必要的复杂性。在许多情况下,只需一个简单的 for 循环即可。

如果这里的 map 函数是IO绑定的,或者正在进行某种计算,这些计算可以从并发中获益,那么解决方案可能会有所不同。例如,NATS可用于超越一台计算机并分配工作负载。

这是一个相对简单的样本。减少阶段不是多阶段并且阻塞:

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)
}

由于它使用interface{}作为元素类型,因此可以进行推广。