在GOlang中使用空接口推广并发映射函数

时间:2017-07-05 23:35:06

标签: go concurrency mapreduce

因此,我对Golang来说是一个超级新手,并且看到围绕该语言的大嗡嗡声似乎是并发性我认为让我的脚趾湿润的好方法是编写一个通用的地图函数。在psudo代码中:

map(F funtion,A array){
    return([F(k) for k in A])
}

显然我希望每个F(k)的计算同时发生。对于组织,我有一个主文件,我的实现和一个支持文件先生我的所需定义。

.
├── main.go
└── Mr
    └── Mr.go

Main是一个简单的测试实现,它应该将一个字符串数组转换为一个int数组,其中结果的每个成员都是输入数组中相应字符串的长度。

package main

import(
    "fmt"
    "./Mr"
)

func exfunc(i int, c chan int){
    c<-i
}

func main(){
    data := make(map[int]string)
    data[1]="these"
    data[2]="are"
    data[3]="some"
    data[4]="words"
    data[5]="and a few more..."
    f := func(w string)int{
        return(len(w))
    }
    testMr := Mr.Map(f,data) // this is line 22 (matters later)
    fmt.Println(testMr)
}

鉴于我明确指定了所有类型,我的Mr.Map函数效果很好。

package Mr

type result struct{
    key,value int
}

func wrapper(f func(string) int,k int,v string, c chan result){
    c <- result{k,f(v)}
}

func Map(f func(string) int,m map[int]string) map[int]int{
    c := make(chan result)
    ret := make(map[int]int)
    n := 0
    for k := range m{
        go wrapper(f,k,m[k],c)
        n++
    }
    for ;n>0; {
        r := <-c
        ret[r.key]=r.value
        n--
    }
    return(ret)
}

但是我希望我能用空接口推广这个映射。

package Mr

type T interface{}

type result struct{
    key,value T
}

func wrapper(f func(T) T,k T,v T, c chan result){
    c <- result{k,f(v)}
}

func Map(f func(T) T,m map[T]T) map[T]T{
    c := make(chan result)
    ret := make(map[T]T)
    n := 0
    for k := range m{
        go wrapper(f,k,m[k],c)
        n++
    }
    for ;n>0; {
        r := <-c
        ret[r.key]=r.value
        n--
    }
    return(ret)
}

不幸的是,当我使用这个概括的Mr.Map运行main时,我得到以下错误。

# command-line-arguments
./main.go:22: cannot use f (type func(string) int) as type func(Mr.T) Mr.T in argument to Mr.Map
./main.go:22: cannot use data (type map[int]string) as type map[Mr.T]Mr.T in argument to Mr.Map

所以是的,显然我明白了错误告诉我的内容,但是我必须为键和值类型的每种可能组合重新编写Map函数似乎很疯狂。

Is there a work around here, or is this just the nature of the beast?

2 个答案:

答案 0 :(得分:1)

那里没有真正的解决方法,这就是野兽的本性。

该语言是在与C ++斗争一段时间后设计的,创作者的想法是简化所有非重要的事情,但同时做出关键的补充,使语言更具表现力。

你可以在这里阅读一下他们的推理,即使你不同意他们做出的所有决定,我相信这很有意思:

https://commandcenter.blogspot.com.ar/2012/06/less-is-exponentially-more.html

在您的示例中,如果您愿意,可以使您的地图和函数使用interface{}(顺便说一句称为空接口,而不是&#34; nil&#34;接口)。

但是你当然会失去编译时类型检查,并且必须在周围添加强制转换。

您还可以尝试找到一个界面来表达您想要使用的类型的共性(可能根本不容易甚至可能),然后围绕该界面构建映射API。

答案 1 :(得分:1)

Go的哲学与广义函数不兼容,例如流行动态语言的风格。为了正确地告知编译器你要做什么,你应该通过一个界面表达你需要的地图,或者只是为你使用它的每种类型写下它。

映射需要分配一个数组,迭代一个集合,并为该集合中的每个元素添加一些数据元素。如果你需要一个结构片的映射,就像应用层中常见的一样,你可以在Go中简洁地表达这个:

https://play.golang.org/p/pk3Tl_BdlD

动态语言构建“通用”类型的“类型树”,允许简洁的编程,例如map之类的函数被任何可能类型的一个符号调用。这提供了大量的开发人员生产力,因为可以松散地编写代码以便于实验。

Go专为编写半永久性软件而设计。它表现良好,因为它需要向编译器提供更多信息。地图只有大约三行代码,因此开发人员生产力与效率的成本/收益落在了Go的性能方面。应根据需要明确写出map,reduce和filter等函数。

为了评估这种语言,我建议您尝试使用Go程序来解决问题并查看它的用途。