Basics
Packages
every go program is made up of packages, and it starts from: package main,
func main()
packages could be imported, and grouped together using parenthesis
(“factored” import statements)
1
2
|
import "fmt"
import "math"
|
same as:
1
2
3
4
|
import (
"fmt"
"math"
)
|
Only capitalized names are exported, therefore after importing a package, you
can only reference its exported names.
Basic Types
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package main
import (
"fmt"
"math/cmplx"
)
// bool
// string
// int int8 int16 int32 int 64
// uint uint8 uint16 uint32 uint64 unitptr
// bytes // alias for uint8
// rune // alias for int32, represents a unicode code point
// float32 float64
// complex64 complex128
var (
ToBe bool = false
MaxInt uint64 = 1<<64 - 1
z complex128 = cmplx.Sqrt(-5 + 12i)
)
func main() {
fmt.Printf("Type: %T Value: %v\n", ToBe, ToBe)
fmt.Printf("Type: %T Value: %v\n", MaxInt, MaxInt)
fmt.Printf("Type: %T Value: %v\n", z, z)
}
|
the int, uint, and uintptr types are usually 32 bits on 32-bit systems and 64
bits on 64-bit systems.
Variables
Two ways to declare a variable:
- var
- with or without initializer (implicitly initialized with zero value)
- package or function scope
:=
short assignment statements
- only function scope
- must have initializers
With an explicit initializer, no need to provide the type of the variable.
Apart from variables, there are constants, which:
- can be declared in package or function level
- must have explicit initializers (also constants, value known at compile time)
- cannot be reassigned after initialization.
Multiple var or const statements could be grouped together like imports.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// with initializer
var i, j int = 1, 2
// omit type (type inference)
var i, j = 1, 2
// short variable declarations (also type inference)
a := 3 // int
b := 3.14 // float64
c := 0.1 + 0.2i
// constants
const Pi = 3.14
// numeric constants are high-precision values
const (
Big = 1 << 100 // 1 << 100
Small = Big >> 99 // 2
)
|
Functions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// type comes after variable name.
func add(x int, y int) int {
return x + y;
}
// consecutive named function parameters share a type
func add(x, y int) int {
return x + y;
}
// function can return any number of results
func swap(x, y string) (string, string) {
return y, x
}
// naked return: a return without arguments, can harm readability in longer
// functions
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
|
Methods
Go has no classes, but you can define methods on types. A method is a
function with a special receiver argument.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
v.Scale(10)
fmt.Println(v.Abs()) // 50
}
|
The method and its receiver type must be defined in the same package.
1
|
type MyFloat float64 // define your own float64 type
|
Pointer Receivers or Arguments
Receiver is just another argument, it can be nil.
If you wanna modify or not copying an argument, you should pass pointer
types.
In general, all methods on a given type should have either value or pointer
receivers, but not a mixture of both.
Type conversions
T(v)
converts the value v
to the type T
.
1
2
3
|
i := 42
f := float64(i)
u := uint(f)
|
no implicit conversion in go
Control FLow
For
Go only has for loop, no while loop, each for loop has three basic
components:
- init statement
- condition statement
- post statement (execute after each iteration)
no parentheses, but curly braces {}
are always required.
1
2
3
4
5
6
7
8
9
10
11
|
package main
import "fmt"
func main() {
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
fmt.Println(sum)
}
|
any of the three components can be omitted, if you only have condition or
nothing left, semicolon can be omitted.
1
2
3
4
5
6
7
8
9
|
sum := 0
// just like while loop
for sum < 1000 {
sum++
}
for {
// loop forever
}
|
you can continue
or break
inside a loop
If
like for
loops, no parentheses ()
, but curly braces {}
are required
can have init statement
1
2
3
4
5
6
7
|
if i := 0; i != 0 {
fmt.Println("what?")
} else if i == 0 {
fmt.Println("got 0")
} else {
fmt.Println("no way here")
}
|
Switch
- shorter way to write a sequence of
if-else
statements.
- no break or default fallthrough in switch
- cases do need not to be constants or integers.
- also support init statements like
if
and for
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.\n", os)
}
}
|
- you can use
fallthrough
keyword to fallthrough
- mutiple statements in a single case:
- omit condition is the same as
switch true
(clean way to write long
if-then-else chains)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package main
import (
"fmt"
"time"
)
func main() {
t := time.Now()
// same as: switch true
switch {
// default will always be last evaluated
default:
fmt.Println("default")
case t.Hour() < 12:
fmt.Println("morning")
case t.Hour() < 17, true: // like useing '||', match any
fmt.Println("afternoon")
fallthrough
case false:
// even condition is false, fallthrough do fallthrough here
fmt.Println("Are you ok?")
// cannot put fallthrough in the last case or default
// fallthrough
}
}
|
Defer
a defer
statement defers the execution of a function until the surrounding
function returns.
arguments evaluated immediately, but function call is not executed until the
surrounding function returns.
defered function calls are pushed onto a stack, so executed in last-in-first-outo order
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package main
import "fmt"
func main() {
fmt.Println("counting")
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
|
Advanced types
Pointers
A pointer holds the memory address of a value.
Go has no pointer arithmetic.
1
2
3
4
|
var p *int
i := 42
p = &i // referencing
fmt.Println(*p) // dereferencing
|
Structs
can be defined inside functions
access struct fileds using a dot
struct fields can also be accessed through a struct pointer, without explicit
dereferencing.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package main
import "fmt"
func main() {
type Vertex struct {
X int
Y int
}
v := Vertex{1, 2}
p := &v // pointer to a struct
v.X = 4
(*p).Y = 6 // dereference the struct first - cumbersum
p.Y = 5 // without explicit dereference
fmt.Println(v)
}
|
struct literal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package main
import "fmt"
type Vertex struct {
X, Y int
}
func main() {
var (
v1 = Vertex{1, 2} // {1, 2}, has type Vertex
v2 = Vertex{Y: 1} // {0, 1}
v3 = Vertex{} // {0, 0}
p = &Vertex{3, 4} // has type *Vertex
)
// {1 2} {0 1} {0 0} &{3 4}
fmt.Println(v1, v2, v3, p)
}
|
Arrays
[n]T
is an array of n
values of type T
.
Arrays cannot be resized
1
2
3
4
5
6
7
8
|
package main
import "fmt"
func main() {
// [1 2 0]
fmt.Println([3]int{1, 2})
}
|
Slices
A slice is a dynamically-sezed, flexible view into the elements of an array.
[n]T
is an array of type T and length n.
[]T
is a slice of type T, it does not store any data, it just describes a
section of the underlying array.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package main
import "fmt"
func main() {
// array literal
v := [5]int{1, 2}
// slice literal
// v := []int{1, 2, 0, 0, 0}
v1 := v[1:3]
fmt.Println(cap(v)) // 5
fmt.Println(cap(v1)) // 4, counting from first element in the slice
fmt.Println(len(v1)) // 2
fmt.Println(v1) // [2 0]
}
|
When slicing, you may omit the high or low bounds to use their defaults
instead (0 for low, len for high)
making a slice
1
2
3
4
|
a := make([]int, 5) // len(a)=5, cap(a)=5
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:] // len(b)=4, cap(b)=4
|
appending to a slice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
func printSlice(s []int) {
fmt.Printf("len=%d, cap=%d, addr=%p, %v\n", len(s), cap(s), &s, s)
}
func main() {
var s []int
printSlice(s) // len=0, cap=0
s = append(s, 0)
printSlice(s) // len=1, cap=1
s = append(s, 1)
printSlice(s) // len=2, cap=2
s = append(s, 1)
printSlice(s) // len=3, cap=4
s = append(s, 1, 2, 3)
printSlice(s) // len=6, cap=8
}
|
if the backing array is too small to fit all the given values a bigger array
will be allocated. The returned slice will point to the newly allocated
array.
range
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package main
import "fmt"
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
// for i, _ := range pow
// for i := range pow
// for _, v := range pow
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
}
|
Maps
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package main
import "fmt"
type Vertex struct {
X, Y int
}
func main() {
var v = map[int]Vertex{
3: {1, 2},
}
// x := v[2]
x, ok := v[2]
// ok is false
if !ok {
fmt.Println("no v[2]")
fmt.Printf("x is the zero value of Vertex, which is %v\n", x)
}
fmt.Println(v[3])
// insert or update an element
v[3] = Vertex{3, 4}
// delete a key
delete(v, 3)
}
|
Function Type
Functions are values too, they can be used as funtion arguments and return
values.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package main
import "fmt"
func f(fn func(int) string, x int) string {
return fn(x)
}
func main() {
myF := func(x int) string {
return "xy"
}
fmt.Println(f(myF, 3))
}
|
Receiver is actually the first argument of a method:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func f(fn func(Vertex) float64, v *Vertex) {
fmt.Println(fn(*v))
}
func f2(fn func(*Vertex) float64, v *Vertex) {
fmt.Println(fn(v))
}
func main() {
v := Vertex{3, 4}
f(Vertex.Abs, &v)
f2((*Vertex).Abs, &v)
// these two are different function
// receiver is actually the first argument of method
fmt.Printf("%T\n", (*Vertex).Scale)
fmt.Printf("%T\n", v.Scale)
}
|
A closure is a function value that references variables from outside its
body.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package main
import "fmt"
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
|
Interfaces
An interface type is a set of method signatures.
An interface value is any type that has implemented those methods
(implemented implicitly, no “implements” keyword).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package main
import (
"fmt"
"math"
)
type Abser interface {
Abs() float64
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat implements Abser
a = &v // a *Vertex implements Abser
// In the following line, v is a Vertex (not *Vertex)
// and does NOT implement Abser.
// a = v
fmt.Println(a.Abs())
}
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
|
printing value and type of an interface is the same as printing its
underlying value and type.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
package main
import "fmt"
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
func main() {
var i I
var t *T
i = t
describe(i)
i.M()
i = &T{"hello"}
describe(i)
i.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
|
The interface that specifies zero methods is known as the empty interface.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package main
import "fmt"
func main() {
var i interface{}
describe(i)
i = 42
describe(i)
i = "hello"
describe(i)
}
func describe(i interface{}) {
fmt.Printf("(%v, %T)\n", i, i)
}
|
Type assertions
try converting an interface to its underlying value of type T: s := i.(T)
here T
must implement methods of i.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package main
import "fmt"
func main() {
var i interface{} = "hello"
s := i.(string)
fmt.Println(s)
s, ok := i.(string)
fmt.Println(s, ok)
f, ok := i.(float64)
fmt.Println(f, ok)
f = i.(float64) // panic
fmt.Println(f)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
package main
import "fmt"
type Vertex struct {
x, y int
}
// value of type *Vertex can also call method M()
func (Vertex) M() {}
func main() {
var v Vertex
var i interface {
M()
} = v
// argument here must implement methods of the interface
t, ok := i.(*Vertex)
des(t) // *main.Vertex, <nil>
chk(t, ok)
t2, ok := i.(Vertex)
des(t2) // main.Vertex, {0, 0}
chk(t2, ok)
}
func des(v interface{}) {
fmt.Printf("%T, %v\n", v, v)
}
func chk(t interface{}, ok bool) {
if !ok {
fmt.Println("type not correct, zero value returned:", t)
} else {
fmt.Println("type correct, value is:", t)
}
}
|
type switches
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package main
import "fmt"
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
// here v has the same type as i
fmt.Printf("I don't know about type %T!\n", v)
}
}
func main() {
do(21)
do("hello")
do(true)
}
|
Zero values
variables declared without an explicit initial value are given their zero
value.
- 0 for numeric types
- false for the boolean type
- "" (the empty string) for the strings
{<default field values>}
for structs
- nil for slice (len and cap of a nil slice is 0)
- nil for maps
- nil for interfaces
- nil for pointers
Common Interfaces
Error
When fmt
prints values, it looks for the error interface first:
1
2
3
|
type error interface {
Error() string
}
|
if the interface value is not <nil>
, the Error() method will be invoked by
fmt
to get the error string.
1
2
3
4
5
6
|
i, err := strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
return
}
fmt.Println("Converted integer:", i)
|
Do not print the interface value in the Error() method directly, it will cause
infinite loop.
1
2
3
4
5
6
7
|
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
// do not print e directly, infinite loop here
// fmt.Println(e)
return fmt.Sprintln("cannot Sqrt negative number: ", float64(e))
}
|
Reader
the io.Reader
interface has a Read
method:
1
|
func (T) Read(b []byte) (n int, err error)
|
read populates the given byte slice with data and returns the number of bytes
populated and an error value.
it returns an io.EOF
error when the stream ends.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package main
import (
"fmt"
"io"
"strings"
)
func main() {
r := strings.NewReader("Hello, Reader!")
b := make([]byte, 8)
for {
n, err := r.Read(b)
fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
fmt.Printf("b[:n] = %q\n", b[:n])
if err == io.EOF {
break
}
}
}
|
Image
image.Image
defines the Image interface
1
2
3
4
5
|
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
|
Goroutines
a goroutine is a lightweight thread managed by the Go runtime.
Channels
By default, sends and receives block until the other side is ready. This
allows goroutines to synchronize without explicit locks or condition
variables.
Channels aren’t like files, you don’t usually need to close them. Closing is
only necessary when the receiver must be told there are no more values
coming, such as to terminate a range
loop.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package main
import "fmt"
func fib(n int, c chan int) {
a, b := 0, 1
for i := 0; i < n; i++ {
c <- a
a, b = b, a+b
}
close(c)
}
func main() {
c := make(chan int)
go fib(10, c)
for x := range c {
fmt.Println(x)
}
// "ok" is false if there are:
// 1. no more values to receive
// 2. and the channel is closed
// x, ok := <- c
}
|
Select
select statement lets a goroutine wait on multiple communication
operations.
select blocks until one of its cases can run (by adding a default case,
it won’t block). It chooses one at random if multiple are ready.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
|
Mutex
sync.Mutex provides two methods: Lock
and Unlock
1
2
3
4
5
6
7
8
9
10
11
12
|
// SafeCounter is safe to use concurrently.
type SafeCounter struct {
mu sync.Mutex
v map[string]int
}
func (c *SafeCounter) Value(key string) int {
c.mu.Lock()
// Lock so only one goroutine at a time can access the map c.v.
defer c.mu.Unlock()
return c.v[key]
}
|
Practice
Sqrt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package main
import "fmt"
func Sqrt(x float64) (res float64) {
res = 1.
diff := 1.
for diff > 1e-5 || diff < -1e-5 {
diff = (res*res - x) / (2 * res)
res -= diff
}
return
}
func main() {
fmt.Println(Sqrt(4))
}
|
WordCount
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package main
import (
"strings"
"golang.org/x/tour/wc"
)
func WordCount(s string) (m map[string]int) {
m = make(map[string]int)
for _, x := range strings.Fields(s) {
m[x]++
}
return m
}
func main() {
wc.Test(WordCount)
}
|
Fibonacci closure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package main
import "fmt"
// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
a, b := 0, 1
return func() int {
ret := a
a, b = b, a + b
return ret
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
|
Sqrt with Error Handling
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package main
import (
"fmt"
"math"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
// float64(e) here is important
// fmt.Sprint(e) will cause infinite loop!
return fmt.Sprint("cannot Sqrt negative number:", float64(e))
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return x, ErrNegativeSqrt(x)
}
return math.Sqrt(x), nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
|
rot13Reader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package main
import (
"io"
"os"
"strings"
)
type rot13Reader struct {
r io.Reader
}
func (rot13reader rot13Reader) Read(b []byte) (int, error) {
n, err := rot13reader.r.Read(b)
if err != nil {
return 0, io.EOF
}
for i := 0; i < n; i++ {
switch c := b[i]; {
case c >= 'A' && c <= 'Z':
b[i] = 'A' + (b[i]-'A'+13)%26
case c >= 'a' && c <= 'z':
b[i] = 'a' + (b[i]-'a'+13)%26
}
}
return n, nil
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
|
Implement Image interface
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package main
import (
"image"
"image/color"
"golang.org/x/tour/pic"
)
type Image struct {
w, h int
}
func (img Image) ColorModel() color.Model {
return color.RGBAModel
}
func (img Image) Bounds() image.Rectangle {
return image.Rect(0, 0, img.w, img.h)
}
func (img Image) At(x, y int) color.Color {
return color.RGBA{uint8(x + y), uint8(x + y), 255, 255}
}
func main() {
m := Image{100, 100}
pic.ShowImage(m)
}
|
Web Crawler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
package main
import (
"fmt"
"sync"
)
type Fetcher interface {
// Fetch returns the body of URL and
// a slice of URLs found on that page.
Fetch(url string) (body string, urls []string, err error)
}
type url2Dep struct {
mu sync.Mutex
mp map[string]int
}
func (u *url2Dep) insertUrl(url string, dep int) {
u.mu.Lock()
defer u.mu.Unlock()
u.mp[url] = dep
}
func (u *url2Dep) getDep(url string) (int, bool) {
u.mu.Lock()
defer u.mu.Unlock()
dep, ok := u.mp[url]
return dep, ok
}
// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(u *url2Dep, url string, depth int, fetcher Fetcher) {
defer wg.Done()
if depth <= 0 {
return
}
if dep, ok := u.getDep(url); !ok || dep < depth {
u.insertUrl(url, depth)
} else {
fmt.Printf("visited: %s\n", url)
return
}
body, urls, err := fetcher.Fetch(url)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("found: %s %q\n", url, body)
for _, nextUrl := range urls {
wg.Add(1)
go Crawl(u, nextUrl, depth-1, fetcher)
}
}
var wg sync.WaitGroup
func main() {
u := url2Dep{mp: make(map[string]int)}
wg.Add(1)
go Crawl(&u, "https://golang.org/", 4, fetcher)
wg.Wait()
}
// fakeFetcher is Fetcher that returns canned results.
type fakeFetcher map[string]*fakeResult
type fakeResult struct {
body string
urls []string
}
func (f fakeFetcher) Fetch(url string) (string, []string, error) {
if res, ok := f[url]; ok {
return res.body, res.urls, nil
}
return "", nil, fmt.Errorf("not found: %s", url)
}
// fetcher is a populated fakeFetcher.
var fetcher = fakeFetcher{
"https://golang.org/": &fakeResult{
"The Go Programming Language",
[]string{
"https://golang.org/pkg/",
"https://golang.org/cmd/",
},
},
"https://golang.org/pkg/": &fakeResult{
"Packages",
[]string{
"https://golang.org/",
"https://golang.org/cmd/",
"https://golang.org/pkg/fmt/",
"https://golang.org/pkg/os/",
},
},
"https://golang.org/pkg/fmt/": &fakeResult{
"Package fmt",
[]string{
"https://golang.org/",
"https://golang.org/pkg/",
},
},
"https://golang.org/pkg/os/": &fakeResult{
"Package os",
[]string{
"https://golang.org/",
"https://golang.org/pkg/",
},
},
}
|