基础知识

作者: Robert Griesemer, Rob Pike, Ken Thompson.

  • 基础架构语言

特被适合构建基础设施类软件如网络服务器,以及程序员使用的工具和系统等。通用语言。

  • CSP Communicating Sequential Process, 通信顺序进程。

hello world

package main

import "fmt"

func main() {
        fmt.Println('hello world!')
}
go run helloworld.go
go build helloworld.go
./helloworld

名为main的包比较特殊,他用来定义一个独立的可执行程序,而不是库。在main 包中,函数main 总是程序开始执行的地方。

无限循环

for {

}

slice

索引使用半开区间,即包含第一个索引,不包含最后一个索引。 slice s[m:n] ,其中 0«m «n « len(s) 包含 n - m 个元素。

echo 将参数输出

echo1.go

package main

import (
        "fmt"
        "os"
)

func main(){
        var s, sep string
        for  i := 1; i< len(os.Args); i++ {
                s += sep + os.Args[i]
                sep = " "
        }
        fmt.Println(s)
}

如果变量没有明确的初始化,它将隐式地初始化这个类型的空值。例如,对于数字初始化是0, 对于字符串是空字符串。 += 是一个赋值操作符

echo2.go

package main

import (
        "fmt"
        "os"
)

func main () {
        s, sep := "", ""
        for _, arg := range os.Args[1:] {
                s += sep + arg
                sep = " "
        }
        fmt.Println(s)
}

空标识符,可以用在任何语法需要变量名但是程序逻辑不需要的地方。例如丢弃每次迭代产生的无用的索引。

几种声明字符串变量的方式

s := "" //短变量的声明方式更加简洁
var s string
var s = ""
var s string = ""
  • map 存储一个键值对集合。map里的键的迭代顺序不是固定的。通常是随机的,每次运行都不一致。 这是有意设计的,以防止程序依赖某种特定的序列。

  • 函数以及其他包级别的实体可以以任意次序声明,比如函数调用可以出现在声明之前。

从文件中统计重复行

  • dup3

    // 统计文件中不重复的行的个数
    package main
        
    import (
            "fmt"
            "io/ioutil"
            "os"
            "strings"
    )
        
    func main() {
            counts := make(map[string]int)
            for _, filename := range os.Args[1:] {
                    data, err := ioutil.ReadFile(filename)
                    fmt.Println(filename)
        
                    if err != nil {
                            fmt.Fprintf(os.Stderr, "dup3: %v\n", err)
                            continue
                    }
                    for _, line := range strings.Split(string(data), "\n"){
                            counts[line]++
                    }
            }
            for line, n := range counts {
                    if n > 1 {
                            fmt.Printf("%d\t%s\n", n, line)
                    }
            }
        
    }
    

gif 动画

  • lissajous 产生随机里萨如图形的gif

    // lissajous 产生随机里萨如图形的GIF 动画
    package main
        
    import (
            "image"
            "image/color"
            "image/gif"
            "io"
            "log"
            "math"
            "math/rand"
            "net/http"
            "os"
            "time"
    )
        
    var palette = []color.Color{color.White, color.Black}
        
    const (
            whiteIndex = 0
            blackIndex = 1
    )
        
    func main() {
            rand.Seed(time.Now().UTC().UnixNano())
            if len(os.Args) > 1 && os.Args[1] == "web" {
                    handler := func(w http.ResponseWriter, r *http.Request) {
                            lissajous(w)
                    }
                    http.HandleFunc("/", handler)
                    log.Fatal(http.ListenAndServe("localhost:8000", nil))
                    return
            }
            lissajous(os.Stdout)
    }
        
    func lissajous(out io.Writer) {
            const (
                    cycles  = 5
                    res     = 0.001
                    size    = 100
                    nframes = 64
                    delay   = 8
            )
            freq := rand.Float64() * 3.0
            anim := gif.GIF{LoopCount: nframes}
            phase := 0.0
            for i := 0; i < nframes; i++ {
                    rect := image.Rect(0, 0, 2*size+1, 2*size+1)
                    img := image.NewPaletted(rect, palette)
                    for t := 0.0; t < cycles*2*math.Pi; t += res {
                            x := math.Sin(t)
                            y := math.Sin(t*freq + phase)
                            img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), blackIndex)
                    }
                    phase += 0.1
                    anim.Delay = append(anim.Delay, delay)
                    anim.Image = append(anim.Image, img)
            }
            gif.EncodeAll(out, &anim)
    }
    

fetch 获取

fetch 从URL获取的内容

// fetch 输出从URL 获取到的内容。
package main

import (
        "fmt"
        "io/ioutil"
        "net/http"
        "os"
)

func main() {
        for _, url := range os.Args[1:] {
                resp, err := http.Get(url)
                if err != nil {
                        fmt.Fprintf(os.Stderr, "fetch: %v \n", err)
                        os.Exit(1)
                }
                b, err := ioutil.ReadAll(resp.Body)
                resp.Body.Close()
                if err != nil {
                        fmt.Fprintf(os.Stderr, "fetch: reading %s : %v\n", url, err)
                        os.Exit(1)
                }
                fmt.Printf("%s", b)
        }
}

fetchall 从多个url获取的内容

//fetchall 并法获取URL 并报告他们的时间和大小
package main

import (
        "fmt"
        "io"
        "io/ioutil"
        "net/http"
        "os"
        "time"
)

func main(){
        start := time.Now()
        ch := make(chan string)
        for _, url := range os.Args[1:] {
                go fetch(url, ch)
        }
        for range os.Args[1:] {
                fmt.Println(<-ch)
        }
        fmt.Printf("%.3fs elapsed\n", time.Since(start).Seconds())
}

func fetch(url string, ch chan <- string) {
        start := time.Now()
        resp, err := http.Get(url)
        if err != nil {
                ch <- fmt.Sprint(err)
                return
        }

        nbytes, err := io.Copy(ioutil.Discard, resp.Body)
        resp.Body.Close()
        if err != nil {
                ch <- fmt.Sprintf("While reading %s: %v", url, err)
                return
        }

        secs := time.Since(start).Seconds()
        ch <- fmt.Sprintf("%.3fs %7d %s", secs, nbytes, url)
}

fetchall2 从文件中的url获取的内容

//fetchall 并法获取URL 并报告他们的时间和大小
package main

import (
        "fmt"
        "io"
        "io/ioutil"
        "net/http"
        "os"
        "strings"
        "time"
)

func main() {
        start := time.Now()
        ch := make(chan string)
        filename := os.Args[1]
        data, err := ioutil.ReadFile(filename)
        if err != nil {
                fmt.Fprintf(os.Stderr, "fetchall_2:%v \n", err)
        }

        l_ := strings.Split(string(data), "\n")
        length_ := len(l_)

        for _, line := range l_ {
                go fetch(line, ch)
        }
        for i := 0; i < length_; i++ {
                fmt.Println(<-ch)
        }
        fmt.Printf("%.3fs elapsed\n", time.Since(start).Seconds())
}

func fetch(url string, ch chan<- string) {
        start := time.Now()
        resp, err := http.Get(url)
        if err != nil {
                ch <- fmt.Sprint(err)
                return
        }

        nbytes, err := io.Copy(ioutil.Discard, resp.Body)
        resp.Body.Close()
        if err != nil {
                ch <- fmt.Sprintf("While reading %s: %v", url, err)
                return
        }

        secs := time.Since(start).Seconds()
        ch <- fmt.Sprintf("%.3fs %7d %s", secs, nbytes, url)
}

一个 web 服务器

server1.go 返回请求路径

// server1
package main

import (
        "fmt"
        "log"
        "net/http"
)

func main(){
        http.HandleFunc("/", handler)
        log.Fatal(http.ListenAndServe("localhost:8000",nil))
}

func handler (w http.ResponseWriter, r *http.Request){
        fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}

server2.go 一个迷你回声和计数服务器

//  server2 是一个迷你回声和计数服务器
package main

import (
        "fmt"
        "log"
        "net/http"
        "sync"
)

var mu sync.Mutex
var count int

func main(){
        http.HandleFunc("/", handler)
        http.HandleFunc("/count", counter)
        log.Fatal(http.ListenAndServe("localhost:8000", nil))
}

func handler (w http.ResponseWriter, r *http.Request) {
        mu.Lock()
        count++
        mu.Unlock()
        fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}

func counter(w http.ResponseWriter, r *http.Request) {
        mu.Lock()
        fmt.Fprintf(w, "Count %d\n", count)
        mu.Unlock()
}

server3.go 处理程序回显HTTP 请求

// 处理程序回显 HTTP 请求
package main

import (
        "fmt"
        "net/http"
        "log"
)

func main () {
        http.HandleFunc("/",handler)
        log.Fatal(http.ListenAndServe("localhost:8000", nil))
}

func handler (w http.ResponseWriter, r *http.Request){
        fmt.Fprintf(w, "%s %s %s\n", r.Method, r.URL, r.Proto)
        for k,v := range r.Header {
                fmt.Fprintf(w, "Header[%q] = %q\n", k, v)
        }
        fmt.Fprintf(w, "Host = %q\n", r.Host)
        fmt.Fprintf(w, "RemoteAddr = %q\n", r.RemoteAddr)
        if err := r.ParseForm(); err != nil {
                log.Print(err)
        }
        for k, v := range r.Form {
                fmt.Fprintf(w, "Form[%q] = %q\n", k, v)
        }
}

server4.go 同过网页显示 lissajous 图形

//server4 通过浏览器显示 lissajous 图形
package main

import (
        "image"
        "image/color"
        "image/gif"
        "io"
        "log"
        "time"
        "math"
        "math/rand"
        "net/http"
)

var palette = []color.Color{color.White, color.Black}

const (
        whiteIndex = 0
        blackIndex = 1
)

func main() {
        rand.Seed(time.Now().UTC().UnixNano())
        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
                lissajous(w)
        })
        log.Fatal(http.ListenAndServe("localhost:8000", nil))
}

func lissajous(out io.Writer) {
        const (
                cycles  = 5
                res     = 0.001
                size    = 100
                nframes = 64
                delay   = 8
        )
        freq := rand.Float64() * 3.0
        anim := gif.GIF{LoopCount: nframes}
        phase := 0.0
        for i := 0; i < nframes; i++ {
                rect := image.Rect(0, 0, 2*size+1, 2*size+1)
                img := image.NewPaletted(rect, palette)
                for t := 0.0; t < cycles*2*math.Pi; t += res {
                        x := math.Sin(t)
                        y := math.Sin(t*freq + phase)
                        img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), blackIndex)
                }
                phase += 0.1
                anim.Delay = append(anim.Delay, delay)
                anim.Image = append(anim.Image, img)
        }
        gif.EncodeAll(out, &anim)
}

控制流

switch coinflip() {
case "heads":
        heads++
case "tails":
        tails++
default:
        fmt.Println("landed on edge!")
}
  • case 语句从上到下推演
  • 默认 case 语句可以放在任何地方。
  • 如果没有其他的case 语句符合条件,那么可选的默认case 语句将被执行
  • switch 语句不需要操作数,它就像一个case 语句列表,每条case 语句都是一个布尔表达式
  • 这种形式称为无标签tagless 选择,它等价于 switch true

    func Signum(x int) int {
            switch {
            case x > 0:
                    return +1
            default:
                    return 0
            case x < 0:
                    return 0
            }
    }
    

实现 Stringer interface

// Filename: go.org[*Org Src go.org[ go ]*]
// coding:utf-8
// 
import (
        "fmt"
)

type human struct{
        name string
}

func (h *human) String () string {
        return fmt.Sprint(h.name, h.name)
}

func main () {
        var h = human{name:"liu"}
        fmt.Println("%s" ,&h)
}

bytecounter

// Filename: go.org[*Org Src go.org[ go ]*]
// coding:utf-8
// ch7/bytecounter
import (
        "fmt"
)
type ByteCounter int

// 
func (c *ByteCounter) Write (p [] byte) (int, error) {
        *c += ByteCounter(len(p))
        return len(p), nil
}

func main () {
        var c ByteCounter
        c.Write([]byte("hello"))
        fmt.Println(c)

        c = 0
        name := "Dolly"
        fmt.Fprintf(&c, "hello, %s", name)
        fmt.Println(c)
}

geometry

// Filename: go.org[*Org Src go.org[ go ]*]
// coding:utf-8
// geometry
import (
        "fmt"
        "math"
)

type Point struct { X, Y float64}
// Path 是连接多个点的直线段
type Path []Point

// Distance 方法返回路径的长度
func (path Path) Distance () float64 {
        sum := 0.0
        for i := range path {
                if i > 0 {
                        sum += path[i - 1].Distance(path[i])
                }
        }
        return sum 
}

// 普通的函数
func Distance (p, q Point) float64 {
        return math.Hypot(p.X - q.X, p.Y - q.Y)
}

// Point 类型的方法
func (p *Point) Distance (q Point) float64 {
        return math.Hypot(p.X - q.X, p.Y - q.Y)
}

// 对点的x,y 成比例放大或缩小
func (p *Point) ScaleBy (factor float64) {
        p.X *= factor
        p.Y *= factor
}

// 格式化输出
func (p *Point) String () string{
        return fmt.Sprintf("X: %.2f, Y: %.2f", p.X, p.Y)
}

func main () {
        p := Point{1,2}
        q := Point{4,6}
        fmt.Println(Distance(p,q))
        fmt.Println(p.Distance(q))


        perim := Path{
                {1,1},
                {5,1},
                {5,4},
                {1,1},
        }
        fmt.Println(perim.Distance())

        // 指针接受者的方法
        r := &Point{1, 1}
        r.ScaleBy(10)
        fmt.Println(*r)
        fmt.Println(r)

        p1 := Point{1,2}
        pptr := &p1
        pptr.ScaleBy(10)
        fmt.Println(p1)
        fmt.Println(pptr)

        p2 := Point{1,3}
        (&p2).ScaleBy(10)
        fmt.Println(p2)
        fmt.Println(&p2)

        p3 := Point{1,4}
        p3.ScaleBy(10)
        fmt.Println(p3)
        fmt.Println(&p3)
}

fib 函数

// Filename: go.org[*Org Src go.org[ go ]*<2>]
// coding:utf-8
// fib

import (
        "fmt"
)

// fib
func fib (n int) (x int) {
        x, y := 0, 1
        for i := 1; i < n ; i++ {
                x, y = y, x + y 
        }
        return 
}

func main () {
        fmt.Println(fib(1000000))	
}

数据类型

basic type

  • 基础类型

aggregate type

  • 聚合类型

reference type

  • 引用类型

interface type

  • 接口类型