I have the following package to generate combinations from a slice of bytes:
//Package combinations generates combinations from a slice.
package combinations
//CombIter generates combinations of bytes of given length from a
//list of elements. It returns values by a channel outC
//An empty list indicates that no further combinations are available
func CombIter(elements []byte, length int, outC chan []byte) {
for i := 0; i < len(elements); i++ {
if length == 1 {
outC <- []byte{elements[i]}
} else {
inC := make(chan []byte)
go combIter(elements[i+1:], length-1, inC)
for next := <-inC; len(next) != 0; next = <-inC {
outC<- append([]byte{elements[i]}, next...)
}
}
}
outC <- []byte{}
}
//Comb collects combinations of length k from a byte slice l.
//It returns a slice containing (len(l) C k) slices all of length k
func Comb(l []byte, k int) [][]byte {
c := make(chan []byte)
go combIter(l, k, c)
o := [][]byte{}
for next := <-c; len(next) != 0; {
o = append(o, next)
next = <-c
}
return o
}
I test it with
func TestComb(t *testing.T)
actual := Comb([]byte{'a', 'b', 'c', 'd'}, 2)
expected := [][]byte{{'a', 'b'}, {'a', 'c'}, {'a', 'd'}, {'b', 'c'}, {'b', 'd'}, {'c', 'd'}}
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Got %v\nWant %v", actual, expected)
}
}
The algorithm is an adaption of a python program: https://stackoverflow.com/a/2837693/2709093
I am particularly concerned with the use of concurrency, and the possibility of creating a disruptive number of goroutines. Is this a useful application of concurrency?