Go 将通道的所有元素消耗到一个切片中
如何从通道中消耗的所有元素中构造一个切片(就像Python那样)?我可以使用这个助手函数:Go 将通道的所有元素消耗到一个切片中,go,channels,Go,Channels,如何从通道中消耗的所有元素中构造一个切片(就像Python那样)?我可以使用这个助手函数: func ToSlice(c chan int) []int { s := make([]int, 0) for i := range c { s = append(s, i) } return s } 但是由于这个原因,我必须为每种类型都写,不是吗?是否有实现此功能的内置函数?如果没有,我如何避免为我正在使用的每种类型复制和粘贴上述代码?您可以使ToSl
func ToSlice(c chan int) []int {
s := make([]int, 0)
for i := range c {
s = append(s, i)
}
return s
}
但是由于这个原因,我必须为每种类型都写,不是吗?是否有实现此功能的内置函数?如果没有,我如何避免为我正在使用的每种类型复制和粘贴上述代码?您可以使ToSlice()只在接口{}上工作,但您在此处保存的代码量可能会在其他地方增加复杂性
func ToSlice(c chan interface{}) []interface{} {
s := make([]interface{}, 0)
for i := range c {
s = append(s, i)
}
return s
}
完整示例
话虽如此,正如@Volker在您展示的代码片段(哈哈)评论中所说的那样,以流式方式处理结果或在生成器处“缓冲”结果并将片段发送到频道下似乎更明智。如果您的代码中只有少数实例需要转换,那么,将7行代码复制几次(甚至在使用它的地方将其内联,这将它减少到4行代码,并且可能是最可读的解决方案)绝对没有错 如果你真的在很多类型的通道和切片之间进行了转换,并且想要一些通用的东西,那么你可以通过反射来实现这一点,但代价是在ChanToSlice的callsite上丑陋且缺少静态类型 下面是一个完整的示例代码,演示如何使用reflect解决这个问题,并演示它在int通道中的工作情况
package main
import (
"fmt"
"reflect"
)
// ChanToSlice reads all data from ch (which must be a chan), returning a
// slice of the data. If ch is a 'T chan' then the return value is of type
// []T inside the returned interface.
// A typical call would be sl := ChanToSlice(ch).([]int)
func ChanToSlice(ch interface{}) interface{} {
chv := reflect.ValueOf(ch)
slv := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(ch).Elem()), 0, 0)
for {
v, ok := chv.Recv()
if !ok {
return slv.Interface()
}
slv = reflect.Append(slv, v)
}
}
func main() {
ch := make(chan int)
go func() {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}()
sl := ChanToSlice(ch).([]int)
fmt.Println(sl)
}
主程序包
进口(
“fmt”
“反映”
)
//ChanToSlice从ch(必须是chan)读取所有数据,并返回
//数据切片。如果ch是“T chan”,则返回值的类型为
//[]T在返回的接口中。
//典型的调用是sl:=ChanToSlice(ch)。([]int)
func ChanToSlice(ch接口{})接口{}{
chv:=反射值(ch)
slv:=reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(ch.Elem()),0,0)
为了{
v、 ok:=chv.Recv()
如果!好的{
返回slv.Interface()
}
slv=reflect.Append(slv,v)
}
}
func main(){
ch:=制造(成交量)
go func(){
对于i:=0;i<10;i++{
ch没有用于此的内置项。这样做实际上看起来很奇怪:当您想将项目从一个goroutine逐个传递到另一个goroutine时,您将使用通道,如果项目形成一个整体并一起处理,则将使用切片。@Volker我不确定为什么这会很奇怪-如果我需要处理来自另一个goroutine的项目,该怎么办总体goroutine?我有一个goroutine可以输出数据点(通常,这些数据点会进入另一个goroutine),但我需要在两个过程中处理所有项目,所以我认为在这里使用切片是更好的选择。这个goroutine只适用于chan接口{}
虽然如此,所以仍然可能不会做他想要的事情。@JamesHenstridge True。这是不起作用的代码:c:=make(chan int);s:=ToSlice(c)
。要使Go成为可能,需要允许从[]int
到[]interface{}
或chan int
到chan interface{}的类型转换
。它不允许这种转换。@Grzegorz&James是的,当然chan也必须是一个接口{}。play链接也有。正如我所说,我不建议这样做。你可以添加最大容量来切片,这应该会稍微有点效率。不太难。s:=make([]接口{},0,len(c))