13

如何从通道消耗的所有元素中构造一个切片(就像 Pythonlist一样)?我可以使用这个辅助函数:

func ToSlice(c chan int) []int {
    s := make([]int, 0)
    for i := range c {
        s = append(s, i)
    }
    return s
}

但由于缺乏泛型,我必须为每种类型都写,不是吗?是否有实现此功能的内置函数?如果没有,我怎样才能避免为我使用的每种类型复制和粘贴上述代码?

4

2 回答 2

8

如果您的代码中只有少数需要转换的实例,那么将这 7 行代码复制几次(或者甚至将其内联到使用它的地方,这将其减少到 4 行代码并且可能是最易读的解决方案)。

如果您确实在很多类型的通道和切片之间进行了转换,并且想要一些通用的东西,那么您可以通过反射来做到这一点,但代价是丑陋和在 ChanToSlice 的调用点缺少静态类型。

这是完整的示例代码,说明如何使用反射来解决此问题,并演示了它适用于 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)
}
于 2013-12-05T09:39:52.797 回答
6

您可以让 ToSlice() 仅在 interface{} 上工作,但您在此处保存的代码量可能会在其他地方花费您的复杂性。

func ToSlice(c chan interface{}) []interface{} {
    s := make([]interface{}, 0)
    for i := range c {
        s = append(s, i)
    }
    return s
}

http://play.golang.org/p/wxx-Yf5ESN上的完整示例

话虽这么说:正如@Volker 在您展示的代码片段(哈哈)的评论中所说,似乎以流方式处理结果或在生成器“缓冲”并发送切下通道。

于 2013-12-05T01:54:05.767 回答