FAQ
I solved http://tour.golang.org/#70 with the following code, and for
my Zip function in particular, the use of select does not feel like
idiomatic Go.
Ideally, I would prefer something like:
v1, v2, ok = <-c1, <- c2
that blocks only if neither c1 nor c2 is closed, and returns
immediately with ok = false if either/both is closed.
Otherwise, I'd have to rewrite this as a fold to have O(1) lines of
code for interleaving more than 2 channels, and it it seems like there
should be a built-in or library version of this.
Thanks in advance for any help. I am still a complete noobie in Go.

// My solution to http://tour.golang.org/#70
package main

import (
"code.google.com/p/go-tour/tree"
"fmt"
)

type Pair struct {
First int
Second int
}

// Interleaves channels c1 and c2 into one channel c3
// Closes c3 when either c1 or c2 is closed
func Zip(c1, c2 chan int, c3 chan Pair) {
var v1, v2 int
var f1, f2 bool
for {
select {
case v1, f1 = <-c1:
if !f1 {
close(c3)
return
}
// We successfully read from c1, wait for c2
v2, f2 = <-c2
if !f2 {
close(c3)
return
}
case v2, f2 = <-c2:
if !f2 {
close(c3)
return
}
// We successfully read from c2, wait for c1
v1, f1 = <-c1
if !f1 {
close(c3)
return
}
}
c3 <- Pair{v1, v2}
}
}

// Flattens a tree into a channel
func Unfold(t *tree.Tree, c chan int, label int) {
if t != nil {
Unfold(t.Left, c, label)
fmt.Printf("Pushing[%v] %v\n", label, t.Value)
c <- t.Value
Unfold(t.Right, c, label)
}
}

// Walks a tree, writes values in preorder into a channel
func Walk(t *tree.Tree, c chan int, label int) {
Unfold(t, c, label)
close(c)
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
fmt.Println("WALK 1")
c1 := make(chan int)
go Walk(t1, c1, 1)

fmt.Println("WALK 2")
c2 := make(chan int)
go Walk(t2, c2, 2)

fmt.Println("ZIP")
c3 := make(chan Pair)
go Zip(c1, c2, c3)

for pair := range c3 {
fmt.Printf("%d\t%d\n", pair.First, pair.Second)
if pair.First != pair.Second {
return false
}
}
return true
}

func main() {
t1 := tree.New(2)
t2 := tree.New(2)
sm := Same(t1, t2)
fmt.Printf("Done with %v\n", sm)
}

--

Search Discussions

  • Thomas Bushnell, BSG at Nov 28, 2012 at 2:17 pm

    On Wed, Nov 28, 2012 at 12:45 AM, Dan Weston wrote:

    I solved http://tour.golang.org/#70 with the following code, and for
    my Zip function in particular, the use of select does not feel like
    idiomatic Go.
    Agreed. :)

    Don't use select at all. Your Walk function is nicely written in such a way
    that readers on c1 and c2 can be guaranteed that receives will eventually
    return no matter what. So just go ahead and block. There is no need for any
    select at all. Also, you don't need a separate goroutine to run Zip in; you
    can just run it directly in Same.

    Ideally, I would prefer something like:
    v1, v2, ok = <-c1, <- c2
    that blocks only if neither c1 nor c2 is closed, and returns
    immediately with ok = false if either/both is closed.
    Otherwise, I'd have to rewrite this as a fold to have O(1) lines of
    code for interleaving more than 2 channels, and it it seems like there
    should be a built-in or library version of this.
    Thanks in advance for any help. I am still a complete noobie in Go.

    // My solution to http://tour.golang.org/#70
    package main

    import (
    "code.google.com/p/go-tour/tree"
    "fmt"
    )

    type Pair struct {
    First int
    Second int
    }

    // Interleaves channels c1 and c2 into one channel c3
    // Closes c3 when either c1 or c2 is closed
    func Zip(c1, c2 chan int, c3 chan Pair) {
    var v1, v2 int
    var f1, f2 bool
    for {
    select {
    case v1, f1 = <-c1:
    if !f1 {
    close(c3)
    return
    }
    // We successfully read from c1, wait for c2
    v2, f2 = <-c2
    if !f2 {
    close(c3)
    return
    }
    case v2, f2 = <-c2:
    if !f2 {
    close(c3)
    return
    }
    // We successfully read from c2, wait for c1
    v1, f1 = <-c1
    if !f1 {
    close(c3)
    return
    }
    }
    c3 <- Pair{v1, v2}
    }
    }

    // Flattens a tree into a channel
    func Unfold(t *tree.Tree, c chan int, label int) {
    if t != nil {
    Unfold(t.Left, c, label)
    fmt.Printf("Pushing[%v] %v\n", label, t.Value)
    c <- t.Value
    Unfold(t.Right, c, label)
    }
    }

    // Walks a tree, writes values in preorder into a channel
    func Walk(t *tree.Tree, c chan int, label int) {
    Unfold(t, c, label)
    close(c)
    }

    // Same determines whether the trees
    // t1 and t2 contain the same values.
    func Same(t1, t2 *tree.Tree) bool {
    fmt.Println("WALK 1")
    c1 := make(chan int)
    go Walk(t1, c1, 1)

    fmt.Println("WALK 2")
    c2 := make(chan int)
    go Walk(t2, c2, 2)

    fmt.Println("ZIP")
    c3 := make(chan Pair)
    go Zip(c1, c2, c3)

    for pair := range c3 {
    fmt.Printf("%d\t%d\n", pair.First, pair.Second)
    if pair.First != pair.Second {
    return false
    }
    }
    return true
    }

    func main() {
    t1 := tree.New(2)
    t2 := tree.New(2)
    sm := Same(t1, t2)
    fmt.Printf("Done with %v\n", sm)
    }

    --

    --
  • Dan Weston (卫西屯) at Nov 28, 2012 at 7:52 pm
    Thanks Thomas!

    I was confusing two ideas:

    1) one producer never closes channel, e.g. prime number generator
    2) one producer decides to stop producing altogether without closing its
    channel

    This is problem 1, which works just as you describe without select.

    My problem originally was not wrapping the recursive Unfold with a
    non-recursive Walk (which closes the channel). The root (Walk) knows when
    to close the channel, but each leaf (Unfold) does not: exhausting that leaf
    says nothing about other leaves.

    This exercise was very informative. I love how easy it is to use lazy
    producer-consumer semantics in Go!


    On Wed, Nov 28, 2012 at 6:17 AM, Thomas Bushnell, BSG
    wrote:
    On Wed, Nov 28, 2012 at 12:45 AM, Dan Weston wrote:

    I solved http://tour.golang.org/#70 with the following code, and for
    my Zip function in particular, the use of select does not feel like
    idiomatic Go.
    Agreed. :)

    Don't use select at all. Your Walk function is nicely written in such a
    way that readers on c1 and c2 can be guaranteed that receives will
    eventually return no matter what. So just go ahead and block. There is no
    need for any select at all. Also, you don't need a separate goroutine to
    run Zip in; you can just run it directly in Same.

    Ideally, I would prefer something like:
    v1, v2, ok = <-c1, <- c2
    that blocks only if neither c1 nor c2 is closed, and returns
    immediately with ok = false if either/both is closed.
    Otherwise, I'd have to rewrite this as a fold to have O(1) lines of
    code for interleaving more than 2 channels, and it it seems like there
    should be a built-in or library version of this.
    Thanks in advance for any help. I am still a complete noobie in Go.

    // My solution to http://tour.golang.org/#70
    package main

    import (
    "code.google.com/p/go-tour/tree"
    "fmt"
    )

    type Pair struct {
    First int
    Second int
    }

    // Interleaves channels c1 and c2 into one channel c3
    // Closes c3 when either c1 or c2 is closed
    func Zip(c1, c2 chan int, c3 chan Pair) {
    var v1, v2 int
    var f1, f2 bool
    for {
    select {
    case v1, f1 = <-c1:
    if !f1 {
    close(c3)
    return
    }
    // We successfully read from c1, wait for c2
    v2, f2 = <-c2
    if !f2 {
    close(c3)
    return
    }
    case v2, f2 = <-c2:
    if !f2 {
    close(c3)
    return
    }
    // We successfully read from c2, wait for c1
    v1, f1 = <-c1
    if !f1 {
    close(c3)
    return
    }
    }
    c3 <- Pair{v1, v2}
    }
    }

    // Flattens a tree into a channel
    func Unfold(t *tree.Tree, c chan int, label int) {
    if t != nil {
    Unfold(t.Left, c, label)
    fmt.Printf("Pushing[%v] %v\n", label, t.Value)
    c <- t.Value
    Unfold(t.Right, c, label)
    }
    }

    // Walks a tree, writes values in preorder into a channel
    func Walk(t *tree.Tree, c chan int, label int) {
    Unfold(t, c, label)
    close(c)
    }

    // Same determines whether the trees
    // t1 and t2 contain the same values.
    func Same(t1, t2 *tree.Tree) bool {
    fmt.Println("WALK 1")
    c1 := make(chan int)
    go Walk(t1, c1, 1)

    fmt.Println("WALK 2")
    c2 := make(chan int)
    go Walk(t2, c2, 2)

    fmt.Println("ZIP")
    c3 := make(chan Pair)
    go Zip(c1, c2, c3)

    for pair := range c3 {
    fmt.Printf("%d\t%d\n", pair.First, pair.Second)
    if pair.First != pair.Second {
    return false
    }
    }
    return true
    }

    func main() {
    t1 := tree.New(2)
    t2 := tree.New(2)
    sm := Same(t1, t2)
    fmt.Printf("Done with %v\n", sm)
    }

    --

    --
  • Thomas Bushnell, BSG at Nov 28, 2012 at 8:57 pm
    The Tour is really nice about pushing you in several different styles of
    using channels, all of which are correct.

    One of the meta-lessons then is how important it is to document clearly in
    comments what the expected behavior of a channel is for things like this,
    especially with exported functions, because there isn't any one universal
    convention.

    On Wed, Nov 28, 2012 at 11:52 AM, Dan Weston (卫西屯) wrote:

    Thanks Thomas!

    I was confusing two ideas:

    1) one producer never closes channel, e.g. prime number generator
    2) one producer decides to stop producing altogether without closing its
    channel

    This is problem 1, which works just as you describe without select.

    My problem originally was not wrapping the recursive Unfold with a
    non-recursive Walk (which closes the channel). The root (Walk) knows when
    to close the channel, but each leaf (Unfold) does not: exhausting that leaf
    says nothing about other leaves.

    This exercise was very informative. I love how easy it is to use lazy
    producer-consumer semantics in Go!


    On Wed, Nov 28, 2012 at 6:17 AM, Thomas Bushnell, BSG <
    tbushnell@google.com> wrote:
    On Wed, Nov 28, 2012 at 12:45 AM, Dan Weston wrote:

    I solved http://tour.golang.org/#70 with the following code, and for
    my Zip function in particular, the use of select does not feel like
    idiomatic Go.
    Agreed. :)

    Don't use select at all. Your Walk function is nicely written in such a
    way that readers on c1 and c2 can be guaranteed that receives will
    eventually return no matter what. So just go ahead and block. There is no
    need for any select at all. Also, you don't need a separate goroutine to
    run Zip in; you can just run it directly in Same.

    Ideally, I would prefer something like:
    v1, v2, ok = <-c1, <- c2
    that blocks only if neither c1 nor c2 is closed, and returns
    immediately with ok = false if either/both is closed.
    Otherwise, I'd have to rewrite this as a fold to have O(1) lines of
    code for interleaving more than 2 channels, and it it seems like there
    should be a built-in or library version of this.
    Thanks in advance for any help. I am still a complete noobie in Go.

    // My solution to http://tour.golang.org/#70
    package main

    import (
    "code.google.com/p/go-tour/tree"
    "fmt"
    )

    type Pair struct {
    First int
    Second int
    }

    // Interleaves channels c1 and c2 into one channel c3
    // Closes c3 when either c1 or c2 is closed
    func Zip(c1, c2 chan int, c3 chan Pair) {
    var v1, v2 int
    var f1, f2 bool
    for {
    select {
    case v1, f1 = <-c1:
    if !f1 {
    close(c3)
    return
    }
    // We successfully read from c1, wait for c2
    v2, f2 = <-c2
    if !f2 {
    close(c3)
    return
    }
    case v2, f2 = <-c2:
    if !f2 {
    close(c3)
    return
    }
    // We successfully read from c2, wait for c1
    v1, f1 = <-c1
    if !f1 {
    close(c3)
    return
    }
    }
    c3 <- Pair{v1, v2}
    }
    }

    // Flattens a tree into a channel
    func Unfold(t *tree.Tree, c chan int, label int) {
    if t != nil {
    Unfold(t.Left, c, label)
    fmt.Printf("Pushing[%v] %v\n", label, t.Value)
    c <- t.Value
    Unfold(t.Right, c, label)
    }
    }

    // Walks a tree, writes values in preorder into a channel
    func Walk(t *tree.Tree, c chan int, label int) {
    Unfold(t, c, label)
    close(c)
    }

    // Same determines whether the trees
    // t1 and t2 contain the same values.
    func Same(t1, t2 *tree.Tree) bool {
    fmt.Println("WALK 1")
    c1 := make(chan int)
    go Walk(t1, c1, 1)

    fmt.Println("WALK 2")
    c2 := make(chan int)
    go Walk(t2, c2, 2)

    fmt.Println("ZIP")
    c3 := make(chan Pair)
    go Zip(c1, c2, c3)

    for pair := range c3 {
    fmt.Printf("%d\t%d\n", pair.First, pair.Second)
    if pair.First != pair.Second {
    return false
    }
    }
    return true
    }

    func main() {
    t1 := tree.New(2)
    t2 := tree.New(2)
    sm := Same(t1, t2)
    fmt.Printf("Done with %v\n", sm)
    }

    --

    --

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedNov 28, '12 at 2:02p
activeNov 28, '12 at 8:57p
posts4
users2
websitegolang.org

People

Translate

site design / logo © 2017 Grokbase