| // Go's concurrency primitives make it easy to |
| // express concurrent concepts, such as |
| // this binary tree comparison. |
| // |
| // Trees may be of different shapes, |
| // but have the same contents. For example: |
| // |
| // 4 6 |
| // 2 6 4 7 |
| // 1 3 5 7 2 5 |
| // 1 3 |
| // |
| // This program compares a pair of trees by |
| // walking each in its own goroutine, |
| // sending their contents through a channel |
| // to a third goroutine that compares them. |
| |
| package main |
| |
| import ( |
| "fmt" |
| "math/rand" |
| ) |
| |
| // A Tree is a binary tree with integer values. |
| type Tree struct { |
| Left *Tree |
| Value int |
| Right *Tree |
| } |
| |
| // Walk traverses a tree depth-first, |
| // sending each Value on a channel. |
| func Walk(t *Tree, ch chan int) { |
| if t == nil { |
| return |
| } |
| Walk(t.Left, ch) |
| ch <- t.Value |
| Walk(t.Right, ch) |
| } |
| |
| // Walker launches Walk in a new goroutine, |
| // and returns a read-only channel of values. |
| func Walker(t *Tree) <-chan int { |
| ch := make(chan int) |
| go func() { |
| Walk(t, ch) |
| close(ch) |
| }() |
| return ch |
| } |
| |
| // Compare reads values from two Walkers |
| // that run simultaneously, and returns true |
| // if t1 and t2 have the same contents. |
| func Compare(t1, t2 *Tree) bool { |
| c1, c2 := Walker(t1), Walker(t2) |
| for { |
| v1, ok1 := <-c1 |
| v2, ok2 := <-c2 |
| if !ok1 || !ok2 { |
| return ok1 == ok2 |
| } |
| if v1 != v2 { |
| break |
| } |
| } |
| return false |
| } |
| |
| // New returns a new, random binary tree |
| // holding the values 1k, 2k, ..., nk. |
| func New(n, k int) *Tree { |
| var t *Tree |
| for _, v := range rand.Perm(n) { |
| t = insert(t, (1+v)*k) |
| } |
| return t |
| } |
| |
| func insert(t *Tree, v int) *Tree { |
| if t == nil { |
| return &Tree{nil, v, nil} |
| } |
| if v < t.Value { |
| t.Left = insert(t.Left, v) |
| return t |
| } |
| t.Right = insert(t.Right, v) |
| return t |
| } |
| |
| func main() { |
| t1 := New(100, 1) |
| fmt.Println(Compare(t1, New(100, 1)), "Same Contents") |
| fmt.Println(Compare(t1, New(99, 1)), "Differing Sizes") |
| fmt.Println(Compare(t1, New(100, 2)), "Differing Values") |
| fmt.Println(Compare(t1, New(101, 2)), "Dissimilar") |
| } |