【问题标题】:Generalizing Concurrent Map Function with Empty Interface In GOlang在 GOlang 中用空接口泛化并发映射函数
【发布时间】:2017-12-09 19:00:00
【问题描述】:

所以我是 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 运行 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?

【问题讨论】:

  • 虽然您的计数器可以正常工作,但有一个内置类型可以管理它,sync.WaitGroup,值得一看。

标签: go concurrency mapreduce


【解决方案1】:

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

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

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

动态语言构建了一个“通用”类型的“类型树”,允许进行简洁的编程,例如像map 这样的函数被一个符号在任何可能的类型上调用。这提供了大量开发人员的工作效率,因为代码可以写得很松散,以便于进行实验。

Go 专为编写半永久性软件而设计。它执行得很好,因为它需要向编译器提供更多信息。 Map 只有大约三行代码,因此开发人员生产力与效率的成本/收益取决于 Go 的性能方面。 map、reduce 和 filter 等函数应根据需要显式编写。

为了评估这门语言,我鼓励你尝试用 Go 程序解决问题,看看它会带你去哪里。

【讨论】:

  • 谢谢,我只用 Golang 编程了 24 小时,所以这个例子非常有帮助。 (在阅读您的示例之前,我实际上还没有弄清楚如何追加。)
【解决方案2】:

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

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

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

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

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

当然,您会失去编译时类型检查,并且必须到处添加强制类型转换。

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

【讨论】:

  • 嗯,这不是我想要的答案,但这是我所期望的......另外感谢您指出什么叫空界面!
  • @kpie:是的,我想......如果你有时间,你可以阅读这个帖子,它很好地展示了不向语言中添加这种特性背后的哲学:groups.google.com/forum/#!topic/golang-nuts/RKymTuSCHS0跨度>
  • 其实我不是在寻找 map reduce 和 filter。这一切都已经创建(blog.gopheracademy.com/advent-2015/glow-map-reduce-for-golang)。我正在寻找的内容可能位于 unsafe.go (github.com/golang/go/blob/master/src/unsafe/unsafe.go) 但我还没有彻底调查。
  • 值得指出的是,您的 map-reduce 不到 20 行代码。每次你需要实现 m-r 时都写这个是微不足道的——我在 Java DTO 中看到了更多的样板文件,它什么都不做。
  • 这是微不足道的,但一切都是如此。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-08
  • 2017-12-11
  • 2014-02-20
  • 2017-07-16
  • 2019-03-20
  • 1970-01-01
相关资源
最近更新 更多