转到:转换后的频道

Let's say I have an int channel in Go:

theint := make(chan int)

I want to wrap this channel in a new channel called incremented

incremented := make(chan int)

Such that:

go func() { theint <- 1 }
    <- incremented // 2

appended can be assumed to be the only one that reads from the int.

It will work if a run a goroutine in the background

go func() {
   for num := range theint {
      incremented <- num + 1
   }
}

However, I prefer to do it without a goroutine since I can't control it in my context.

Is there a simpler way to do it?

One thing that came to mind is python's yield:

for num in theint:
     yield num + 1

Is something like this possible in go?

Generator pattern

What you are trying to implement is generator pattern. To use channels and goroutines for implementation of this pattern is totally common practice.

However, I prefer to do it without a goroutine since I can't control it in my context.

I believe the problem is deadlock

fatal error: all goroutines are asleep - deadlock!

To avoid deadlocks and orphaned (not closed) channels use sync.WaitGroup. This is an idiomatic way to control goroutines.

Playground

package main

import (
    "fmt"
    "sync"
)

func incGenerator(n []int) chan int {
    ch := make(chan int)
    var wg sync.WaitGroup
    wg.Add(len(n))

    for _, i := range n {
        incremented := i + 1
        go func() {
            wg.Done()
            ch <- incremented
        }()
    }

    go func() {
        wg.Wait()
        close(ch)
    }()

    return ch
}

func main() {
    n := []int{1, 2, 3, 4, 5}
    for x := range incGenerator(n) {
        fmt.Println(x)
    }
}

One thing you can also consider is having a select on the int channel and an exit channel - in an infinite for loop. You can choose a variable increment value too. Please see code below:

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {

    var accum int //accumulator of incremented values
    var wg sync.WaitGroup


    c1 := make(chan int)
    exChan := make(chan bool)

    wg.Add(1)
    go func() {
        time.Sleep(time.Second * 1)
        c1 <- 1
        wg.Done()
    }()
    wg.Add(1)
    go func() {
        time.Sleep(time.Second * 2)
        c1 <- 2
        wg.Done()
    }()
    wg.Add(1)
    go func() {
        time.Sleep(time.Second * 2)
        c1 <- 5
        wg.Done()
    }()
    go func() {
        wg.Wait()
        close(exChan)
    }()

    for {
        var done bool

        select {
        case incBy := <-c1: //Increment by value in channel
            accum += incBy
            fmt.Println("Received value to increment:", incBy, "; Accumulated value is", accum)
        case d := <-exChan:
            done = !(d)
        }
        if done == true {
            break

        }
    }

    fmt.Println("Final accumulated value is", accum)
}

Playground: https://play.golang.org/p/HmdRmMCN7U

Exit channel not needed, if we are having non-zero increments always. I like @I159 's approach too!

Anyways, hope this helps.