将通道的所有元素消耗到一个切片中。

huangapple go评论90阅读模式
英文:

Consuming all elements of a channel into a slice

问题

我可以使用以下帮助函数来构建一个从通道中获取所有元素的切片(类似于Python的 list):

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

但是由于 Go语言缺乏泛型,我需要为每种类型编写类似的函数,对吗?是否有内置函数可以实现这个功能?如果没有,我如何避免为每种类型都复制粘贴上述代码?

英文:

How can I construct a slice out of all of elements consumed from a channel (like Python's list does)? I can use this helper function:

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

but due to the lack of generics, I'll have to write that for every type, won't I? Is there a builtin function that implements this? If not, how can I avoid copying and pasting the above code for every single type I'm using?

答案1

得分: 9

如果你的代码中只有几个地方需要进行转换,那么复制这7行代码几次是没有问题的(甚至可以将其内联到使用它的地方,这样可以将其减少为4行代码,也是最可读的解决方案)。

如果你确实需要在许多类型的通道和切片之间进行转换,并且希望使用一种通用的方法,那么你可以使用反射来解决这个问题,但代价是代码的丑陋和调用ChanToSlice时缺乏静态类型。

下面是一个完整的示例代码,展示了如何使用反射来解决这个问题,并演示了它如何在int通道上工作。

package main

import (
	"fmt"
	"reflect"
)

// ChanToSlice 从ch(必须是一个通道)中读取所有数据,返回一个数据的切片。
// 如果ch是一个'T chan',则返回值是返回接口中的[]T类型。
// 典型的调用方式是 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)
}

希望对你有帮助!

英文:

If there's only a few instances in your code where the conversion is needed, then there's absolutely nothing wrong with copying the 7 lines of code a few times (or even inlining it where it's used, which reduces it to 4 lines of code and is probably the most readable solution).

If you've really got conversions between lots and lots of types of channels and slices and want something generic, then you can do this with reflection at the cost of ugliness and lack of static typing at the callsite of ChanToSlice.

Here's complete example code for how you can use reflect to solve this problem with a demonstration of it working for an int channel.

package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

// ChanToSlice reads all data from ch (which must be a chan), returning a
// slice of the data. If ch is a &#39;T chan&#39; 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 &lt; 10; i++ {
			ch &lt;- i
		}
		close(ch)
	}()
	sl := ChanToSlice(ch).([]int)
	fmt.Println(sl)
}

答案2

得分: 8

你可以使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在评论中所说,从你展示的代码片段中,更明智的做法似乎是以流式处理的方式处理结果,或者在生成器中“缓冲它们”,然后只需将切片发送到通道中。

英文:

You could make ToSlice() just work on interface{}'s, but the amount of code you save here will likely cost you in complexity elsewhere.

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

Full example at http://play.golang.org/p/wxx-Yf5ESN

That being said: As @Volker said in the comments from the slice (haha) of code you showed it seems like it'd be saner to either process the results in a streaming fashion or "buffer them up" at the generator and just send the slice down the channel.

huangapple
  • 本文由 发表于 2013年12月5日 04:41:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/20385464.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定