Golang Basic

Golang语法

  • 无对象、无继承多态、无泛型
  • 有接口,函数式编程,CSP并发模型

变量类型

  • (u)int,(u)int8,(u)int16,(u)int32,(u)int64

    有符号、无符号整数,默认长度同os

  • uintptr 指针

  • bool,string,byte
  • rune字符型,相当于32位char
  • float32,float64
  • complex64,complex128复数

强制转换方法相同

常量类型:const

枚举类型,iota:自增

func enums()  {
    const
    (
        cpp = iota
        java
        python
        php
    )
    fmt.Println(cpp, java, python, php)
}

0 1 2 3
const (
        b = 1<<(10*iota)
        kb
        mb
        gb
        tb
        pb
    )
    fmt.Println(b,kb,mb,gb,tb,pb)

1 1024 1048576 1073741824 1099511627776 1125899906842624

条件语句

go语言函数可以返回两个值

条件语句 if 条件没有括号,条件可以定义变量

import (
    "fmt"
    "io/ioutil"
    )
func main() {

    const filename  = "abc.txt"
    contents, err := ioutil.ReadFile(filename)
    if err!= nil {
        fmt.Println(err)
    }else {
        fmt.Println("%s\n",contents)
    }
}
import (
    "fmt"
    "io/ioutil"
)
func main() {

    const filename  = "abc.txt"
    if contents, err := ioutil.ReadFile(filename) ; err != nil{
        fmt.Println(err)
    }else {
        fmt.Println(contents)
    }
}

switch,没有break

g1

switch后可以没有表达式,在case中加入条件

g2

循环语句

golang没有while

for循环

func convert2bin(n int)  string{
    result := ""
    for ; n > 0 ; n /= 2  {
        lsb := n % 2
        result = strconv.Itoa(lsb) + result
    }
    return result
}

g3

死循环

for{
    fmt.Println("abc")
}

函数

函数可以返回多个值

func div(a,b int) (int, int) {
    return a/b, a%b
}
func divp(a,b int) (q,r int) {
    q = a/b
    r = a%b
    return 
}

定义的值必须用到,如果不想用则使用 _

返回error,方便处理

g4

函数式编程,参数可以是函数

g5

其他用法:可变参数列表

g6

指针

指针不能运算

g7

go语言只有值传递,没有引用传递

交换ab:

func swap(a, b int){
    b, a = a, b
}
swap(a,b)
func swap(a, b *int){
    *b, *a = *a, *b
}
swap(&a,&b)
func swap(a, b int)(int, int){
return b,a
}
a, b = swap(a,b)

容器

Array

定义数组

package main

import "fmt"
func main() {
    var arr1 [3]int
    var arr2 = [3]int {1,2,3}
    arr3 := [...]int {1,2,3,4,5}

    fmt.Println(arr1,arr2,arr3)
}

[0 0 0] [1 2 3] [1 2 3 4 5]
import "fmt"

func main() {
    var grid [4][5]int
    fmt.Println(grid)
}

[[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]

遍历数组

func main() {
    arr3 := [...]int {1,2,3,4,5}
    for i := 0;i<len(arr3) ;i++  {
        fmt.Println(arr3[i])
    }
}

使用range遍历

import "fmt"

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

g8

func main() {
    arr3 := [...]int {1,2,3,4,5}
    for i := range arr3  {
        fmt.Println(i)
    }
    for _,v := range arr3  {
        fmt.Println(v)
    }
}

数组是值类型,不会改变数组本身

go语言一般不直接使用数组

Slice

g9

slice是原本array的一个view,array改变后,slice也跟着改变

import "fmt"

func updateSlice(s []int) {
    s[0] = 100
}

func main() {
    arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}

    fmt.Println("arr[2:6] =", arr[2:6])
    fmt.Println("arr[:6] =", arr[:6])
    s1 := arr[2:]
    fmt.Println("s1 =", s1)
    s2 := arr[:]
    fmt.Println("s2 =", s2)

    fmt.Println("After updateSlice(s1)")
    updateSlice(s1)
    fmt.Println(s1)
    fmt.Println(arr)

    fmt.Println("After updateSlice(s2)")
    updateSlice(s2)
    fmt.Println(s2)
    fmt.Println(arr)

    fmt.Println("Reslice")
    fmt.Println(s2)
    s2 = s2[:5]
    fmt.Println(s2)
    s2 = s2[2:]
    fmt.Println(s2)

    fmt.Println("Extending slice")
    arr[0], arr[2] = 0, 2
    fmt.Println("arr =", arr)
    s1 = arr[2:6]
    s2 = s1[3:5] // [s1[3], s1[4]]
    fmt.Printf("s1=%v, len(s1)=%d, cap(s1)=%d\n",
        s1, len(s1), cap(s1))
    fmt.Printf("s2=%v, len(s2)=%d, cap(s2)=%d\n",
        s2, len(s2), cap(s2))

    s3 := append(s2, 10)
    s4 := append(s3, 11)
    s5 := append(s4, 12)
    fmt.Println("s3, s4, s5 =", s3, s4, s5)
    // s4 and s5 no longer view arr.
    fmt.Println("arr =", arr)

    // Uncomment to run sliceOps demo.
    // If we see undefined: sliceOps
    // please try go run slices.go sliceops.go
    fmt.Println("Uncomment to see sliceOps demo")
    // sliceOps()
}

g11

reslice

g10

slice结构

g12

s[i]超过len会报错越界,向后扩展不能超过cap

slice可以向后扩展,不能向前扩展

slice末尾追加元素,依次追加,但是原始arr到cap不再增加,会自动生成一个更大的数组

g13

内建函数make,代表len,cap

s2 := make([]int, 16)
s3 := make([]int, 10, 32)
package main

import "fmt"

func printSlice(s []int) {
    fmt.Printf("%v, len=%d, cap=%d\n",
        s, len(s), cap(s))
}

func sliceOps() {
    fmt.Println("Creating slice")
    var s []int // Zero value for slice is nil

    for i := 0; i < 100; i++ {
        printSlice(s)
        s = append(s, 2*i+1)
    }
    fmt.Println(s)

    s1 := []int{2, 4, 6, 8}
    printSlice(s1)

    s2 := make([]int, 16)
    s3 := make([]int, 10, 32)
    printSlice(s2)
    printSlice(s3)

    fmt.Println("Copying slice")
    copy(s2, s1)
    printSlice(s2)

    fmt.Println("Deleting elements from slice")
    s2 = append(s2[:3], s2[4:]...)
    printSlice(s2)

    fmt.Println("Popping from front")
    front := s2[0]
    s2 = s2[1:]

    fmt.Println(front)
    printSlice(s2)

    fmt.Println("Popping from back")
    tail := s2[len(s2)-1]
    s2 = s2[:len(s2)-1]

    fmt.Println(tail)
    printSlice(s2)
}

g14

Map

package main

import "fmt"

func main() {
    m := map[string]string{
        "name":    "ccmouse",
        "course":  "golang",
        "site":    "imooc",
        "quality": "notbad",
    }

    m2 := make(map[string]int) // m2 == empty map

    var m3 map[string]int // m3 == nil

    fmt.Println("m, m2, m3:")
    fmt.Println(m, m2, m3)

    fmt.Println("Traversing map m")
    for k, v := range m {
        fmt.Println(k, v)
    }

    fmt.Println("Getting values")
    courseName := m["course"]
    fmt.Println(`m["course"] =`, courseName)
    if causeName, ok := m["cause"]; ok {
        fmt.Println(causeName)
    } else {
        fmt.Println("key 'cause' does not exist")
    }

    fmt.Println("Deleting values")
    name, ok := m["name"]
    fmt.Printf("m[%q] before delete: %q, %v\n",
        "name", name, ok)

    delete(m, "name")
    name, ok = m["name"]
    fmt.Printf("m[%q] after delete: %q, %v\n",
        "name", name, ok)
}

g15

遍历map是无序的

去拿不存在的key,得到zero value空值

可以用causeName, ok := m[“cause”]判断是否存在

map使用哈希表,可以比较相等

除了slice、map、function的内建类型都可以作为key

struct类型也可以作为key

寻找最长不重复子串

abcabcbb -> abc

g16

package main

import (
    "fmt"
)

func lengthOfNonRepeatingSubStr(s string) int {
    lastOccurred := make(map[rune]int)
    start := 0
    maxLength := 0

    for i, ch := range []rune(s) {
        if lastI, ok := lastOccurred[ch]; ok && lastI >= start {
            start = lastI + 1
        }
        if i-start+1 > maxLength {
            maxLength = i - start + 1
        }
        lastOccurred[ch] = i
    }

    return maxLength
}

func main() {
    fmt.Println(
        lengthOfNonRepeatingSubStr("abcabcbb"))
    fmt.Println(
        lengthOfNonRepeatingSubStr("bbbbb"))
    fmt.Println(
        lengthOfNonRepeatingSubStr("pwwkew"))
    fmt.Println(
        lengthOfNonRepeatingSubStr(""))
    fmt.Println(
        lengthOfNonRepeatingSubStr("b"))
    fmt.Println(
        lengthOfNonRepeatingSubStr("abcdef"))
    fmt.Println(
        lengthOfNonRepeatingSubStr("这里是慕课网"))
    fmt.Println(
        lengthOfNonRepeatingSubStr("一二三二一"))
    fmt.Println(
        lengthOfNonRepeatingSubStr(
            "黑化肥挥发发灰会花飞灰化肥挥发发黑会飞花"))
}

中文使用byte会出现问题,因而进行改进

国际化支持:

for i, ch := range []rune(s)

使用range遍历pos,rune对,但是pos不连续

真正的字符数量utf8.RuneCountInString可获得

对象

值接受和指针接受

go对象仅支持封装、不支持继承多态

package tree

import "fmt"

type Node struct {
    Value       int
    Left, Right *Node
}

func (node Node) Print() {
    fmt.Print(node.Value, " ")
}

func (node *Node) SetValue(value int) {
    if node == nil {
        fmt.Println("Setting Value to nil " +
            "node. Ignored.")
        return
    }
    node.Value = value
}

func CreateNode(value int) *Node {
    return &Node{Value: value}
}
package tree

import "fmt"

func (node *Node) Traverse() {
    node.TraverseFunc(func(n *Node) {
        n.Print()
    })
    fmt.Println()
}

func (node *Node) TraverseFunc(f func(*Node)) {
    if node == nil {
        return
    }

    node.Left.TraverseFunc(f)
    f(node)
    node.Right.TraverseFunc(f)
}

func (node *Node) TraverseWithChannel() chan *Node {
    out := make(chan *Node)
    go func() {
        node.TraverseFunc(func(node *Node) {
            out <- node
        })
        close(out)
    }()
    return out
}

无论是指针函数还是值函数,调用上都没有区别

值接受者是go语言特有的

使用指针接受者的情况:

  • 改变内容必须使用指针接收者
  • 结构过大考虑使用指针接收者
  • 如果有指针接收者,最好都采用指针接收者

封装

一般采用驼峰规则

首字母大写表示public,首字母小写表示private

包:

  • 每个目录一个包
  • main包包含可执行入口
  • 为结构定义的方法必须在同一个包内,可以在不同的文件

如何扩充系统的类或者别人的类:

  • 定义别名
  • 使用组合

使用组合:

package main

import (
    "fmt"

    "imooc.com/ccmouse/learngo/tree"
)

type myTreeNode struct {
    node *tree.Node
}

func (myNode *myTreeNode) postOrder() {
    if myNode == nil || myNode.node == nil {
        return
    }

    left := myTreeNode{myNode.node.Left}
    right := myTreeNode{myNode.node.Right}

    left.postOrder()
    right.postOrder()
    myNode.node.Print()
}

func main() {
    var root tree.Node

    root = tree.Node{Value: 3}
    root.Left = &tree.Node{}
    root.Right = &tree.Node{5, nil, nil}
    root.Right.Left = new(tree.Node)
    root.Left.Right = tree.CreateNode(2)
    root.Right.Left.SetValue(4)

    fmt.Print("In-order traversal: ")
    root.Traverse()

    fmt.Print("My own post-order traversal: ")
    myRoot := myTreeNode{&root}
    myRoot.postOrder()
    fmt.Println()

    nodeCount := 0
    root.TraverseFunc(func(node *tree.Node) {
        nodeCount++
    })
    fmt.Println("Node count:", nodeCount)

    c := root.TraverseWithChannel()
    maxNodeValue := 0
    for node := range c {
        if node.Value > maxNodeValue {
            maxNodeValue = node.Value
        }
    }
    fmt.Println("Max node value:", maxNodeValue)
}

通过别名方法:

package queue

// A FIFO queue.
type Queue []int

// Pushes the element into the queue.
//         e.g. q.Push(123)
func (q *Queue) Push(v int) {
    *q = append(*q, v)
}

// Pops element from head.
func (q *Queue) Pop() int {
    head := (*q)[0]
    *q = (*q)[1:]
    return head
}

// Returns if the queue is empty or not.
func (q *Queue) IsEmpty() bool {
    return len(*q) == 0
}

GOPATH

src 源代码

pkg 中间包

bin 可执行文件

建议所有项目和第三方库放在一个GOPATH下

可以通过go get 获取第三方库

墙内通过镜像拉取:

go get -v github.com/gpmgo/gopm

gopm update

gopm get -g -v uri

go build / install

go run

接口

duck typing 描述事物外部行为而非内部结构

接口的定义和实现

接口的定义:接口由使用者定义(与传统相反)

只要实现了相应方法,就被判定为实现了接口,不需要声明实现了哪个接口

package main

import (
    "fmt"
)

type Phone interface {
    call()
}

type NokiaPhone struct {
}

func (nokiaPhone NokiaPhone) call() {
    fmt.Println("I am Nokia, I can call you!")
}

type IPhone struct {
}

func (iPhone IPhone) call() {
    fmt.Println("I am iPhone, I can call you!")
}

func main() {
    var phone Phone

    phone = new(NokiaPhone)
    phone.call()

    phone = new(IPhone)
    phone.call()

}

接口值类型

接口变量可以是实现者类型+实现者的值

或者实现者类型+实现者的指针

g17

所以不需要使用接口的指针

interface{}代表支持任何类型

type  Queue []interface{}

interface强制类型转换

func (q *Queue) Pop() int {
    head := (*q)[0]
    *q = (*q)[1:]
    return head.(int)
}

接口的组合

package main
type Retriever interface {
    Get(url string) string
}

type Poster interface {
    Post(url string,
        form map[string]string) string
}

const url = "http://www.imooc.com"

func download(r Retriever) string {
    return r.Get(url)
}

func post(poster Poster) {
    poster.Post(url,
        map[string]string{
            "name":   "ccmouse",
            "course": "golang",
        })
}

type RetrieverPoster interface {
    Retriever
    Poster
}

func session(s RetrieverPoster) string {
    s.Post(url, map[string]string{
        "contents": "another faked imooc.com",
    })
    return s.Get(url)
}

func main() {
    var r Retriever

    mockRetriever := mock.Retriever{
        Contents: "this is a fake imooc.com"}
    r = &mockRetriever
    inspect(r)

    r = &real.Retriever{
        UserAgent: "Mozilla/5.0",
        TimeOut:   time.Minute,
    }
    inspect(r)

    // Type assertion
    if mockRetriever, ok := r.(*mock.Retriever); ok {
        fmt.Println(mockRetriever.Contents)
    } else {
        fmt.Println("r is not a mock retriever")
    }

    fmt.Println(
        "Try a session with mockRetriever")
    fmt.Println(session(&mockRetriever))
}

func inspect(r Retriever) {
    fmt.Println("Inspecting", r)
    fmt.Printf(" > Type:%T Value:%v\n", r, r)
    fmt.Print(" > Type switch: ")
    switch v := r.(type) {
    case *mock.Retriever:
        fmt.Println("Contents:", v.Contents)
    case *real.Retriever:
        fmt.Println("UserAgent:", v.UserAgent)
    }
    fmt.Println()
}
package mock

import "fmt"

type Retriever struct {
    Contents string
}

func (r *Retriever) String() string {
    return fmt.Sprintf(
        "Retriever: {Contents=%s}", r.Contents)
}

func (r *Retriever) Post(url string,
    form map[string]string) string {
    r.Contents = form["contents"]
    return "ok"
}

func (r *Retriever) Get(url string) string {
    return r.Contents
}

package real

import (
    "net/http"
    "net/http/httputil"
    "time"
)

type Retriever struct {
    UserAgent string
    TimeOut   time.Duration
}

func (r *Retriever) Get(url string) string {
    resp, err := http.Get(url)
    if err != nil {
        panic(err)
    }

    result, err := httputil.DumpResponse(
        resp, true)

    resp.Body.Close()

    if err != nil {
        panic(err)
    }

    return string(result)
}

常用接口stringer,reader,writer等

  • Copyrights © 2019-2020 Rex