在 Golang 中使用空接口泛化并发映射函数

Generalizing Concurrent Map Function with Empty Interface In GOlang

所以我是 Golang 的超级新手,看到围绕该语言的热门话题似乎是并发性,我认为让我的脚趾湿润的好方法是编写一个通用的 map 函数。在伪代码中:

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

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

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

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

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时,我得到以下错误。

# 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?

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

该语言是在与 C++ 斗争了一段时间后设计的,创建者的想法是简化所有非重要的事情,但同时进行关键添加以使语言更具表现力。

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

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

在你的例子中,如果你愿意,你可以让你的地图和函数使用 interface{}(顺便说一下,它被称为空接口而不是 "nil" 接口)。

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

您还可以尝试找到一个接口来表达您要使用的类型的共性(这可能不是那么容易,甚至根本不可能),然后围绕它构建您的映射 API接口.

Go 的哲学与广义函数不兼容,例如流行的动态语言的风格。为了正确地通知编译器你正在尝试做什么,你应该通过接口表达你需要的映射,或者简单地为你正在使用它的每种类型编写它。

映射需要分配一个数组,遍历一个集合,并为集合中的每个元素向数组添加一些数据元素。如果你需要一个结构切片的映射,这在应用程序层很常见,你可以在 Go 中简洁地表达:

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

动态语言构建了 "type tree" 的 "generic" 类型,允许进行简洁的编程,例如 map 之类的函数可以在任何可能的类型上由一个符号调用。这提供了大量的开发人员生产力,因为可以松散地编写代码以允许轻松进行实验。

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

为了评估这门语言,我鼓励您尝试使用 Go 程序解决问题,然后看看它能把您带到哪里。