Go语言学习笔记

Basics

  1. Go语言指针不能进行运算。
  2. Go语言只有值传递一种方式。
  3. 函数式编程(可以将函数作为参数传入函数)例子:
package main

import(
	"fmt"
	"math"
	"reflect"
	"runtime"
)

func apply(op func(int, int) int, a, b int) int {
	p := reflect.ValueOf(op).Pointer()
	opName := runtime.FuncForPC(p).Name()
	fmt.Printf("Calling function %s with args " + "(%d, %d)\n", opName, a, b)
	return op(a, b)
}

func main() {
	fmt.Println(apply(func(a int, b int) int {
		return int(math.Pow(float64(a), float64(b)))
	}, 3, 4))
}
  1. go语言可变参数例子:
func sum(numbers ...int) int {
	s := 0
	for i:= range numbers {
		s += numbers[i]
	}
	return s
}
  1. go语言只可以调用指针,不可以对指针进行运算:
package main

func main() {
	var a int = 2
	var pa *int = &a
	*pa = 3
	fmt.Println(a)
}

切片(Slice)

  1. slice是array的view(视图)
  2. slice可以向后扩展, 不可以向前扩展
  3. s[i]不可以超过len(s), 向后扩展不可以超越底层数组cap(s)
  4. 添加元素时如果超越cap, 系统会重新分配更大的底层数组
  5. 由于值传递的关系, 必须接受append的返回值
  6. s = append(s, val)
func updateSlice(s []int) {
	s[0] = 100
}

func main() {
	arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
	fmt.Println("a[2:6] =", arr[2:6])	//2, 3, 4, 5 半开半闭区间
	fmt.Println("a[:6] =", arr[:6])

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

	fmt.Println("After updateSlice(s1)")
	updateSlice(s1)
	fmt.Println(s1)
	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
	s1 = arr[2:6]
	s2 = arr[3:5]
	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(s2, 11)
	s5 := append(s2, 12)
	fmt.Println("s3, s4, s5 =". s3, s4, s5)
	// s4 and s5 no longer view arr.
	fmt.Println("arr =", arr)
}
func printSlice(s []int){
	fmt.Printf("len=%d, cap=%d\n", len(s), cap(s))
}

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

func main() {
	fmt.Println("Creating slice")

	var s[]int	//Zero Value for slice is nil, s == 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)

	//创建长度为16的slice
	s2 := make([]int, 16)
	
	//创建长度为10, cap为32的slice
	s3 := make([]int, 10, 32)

	printSlice(s2)
	printSlice(s3)

	fmt.Println("copying slice")
	copy(s2, s1)
	printSliceValue(s2)

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

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

	fmt.Println(front)
	printSliceValue(s2)

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

map

  1. 创建map: make(map[string]int)
  2. 获取元素: m[key]
  3. key不存在时,获得value类型的初始值
  4. 用value, ok :=m[key]来判断是否存在key
  5. 用delete删除一个key
  6. 使用range遍历key,或者遍历key, value对
  7. 不保证遍历顺序,如需顺序,需手动对key排序
  8. 使用len获得元素个数
  9. map使用哈希表,必须可以比较相等
  10. 除了slice, map, function, 内建类型都可以作为key
  11. Struct类型不包含slice, map, function, 也可以作为key
func main() {
	//定义一个Map
	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("Traversing map")

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

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

	fmt.Println("Deleting values")
	name, ok3 := m["name"]
	fmt.Println(name, ok3)
	delete(m, "name")
	name, ok3 = m["name"]
	fmt.Println(name, ok3)
}

go语言字符和字符串处理

  1. rune相当于go的char
func main() {
    s := "Yes我爱海能达!"    //UTF-8

    for i, ch := range s {  //ch is a rune
        fmt.Printf("(%d %X) ", i, ch)
    }

    fmt.Println()

    fmt.Println("Rune count:", utf8.RuneCountInString(s))

    bytes := []byte(s)

    for len(bytes) > 0 {
        ch, size := utf8.DecodeRune(bytes)
        bytes = bytes[size:]
        fmt.Printf("%c", ch)
    }

    fmt.Println()

    for i, ch := range []rune(s) {
        fmt.Printf("(%d %c) ", i, ch)
    }

    fmt.Println()
}
  1. 例:寻找最长不含有重复字符的子串
    解:对于每一个字母x,
    lastOccurred[x]不存在, 或者 < start -> 无需操作
    lastOccurred[x] >= start -> 更新start
    更新lastOccurred[x], 更新maxLength
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("abcdefgh"))
    fmt.Println(lengthOfNonRepeatingSubStr("这里是海能达"))
    fmt.Println(lengthOfNonRepeatingSubStr("一二三二一"))
}

结构体和方法

  1. go语言仅支持封装,不支持继承和多态
  2. go语言没有class, 只有struct
  3. 无论地址还是结构本身,一律使用 . 来访问成员
  4. go语言的函数中,局部变量的地址也可以作为函数返回值
  5. 为结构体定义方法时需注意,如果想改变结构内容,要使用指针作为方法接收者。
    只有指针才可以改变结构内容。
  6. 结构过大也考虑使用指针接收者,因为指针接收者拷贝速度比值接收者快
  7. 一致性(建议):如有指针接收者,最好都是指针接收者
  8. 值接收者是go语言特有
  9. 值/指针接收者均可接收值/指针
  10. nil指针也可以调用方法
//maze.go
type point struct{ i, j int }

var dir []point = []point{
	{-1, 0}, {0, -1}, {1, 0}, {0, 1},
}

func main() {
	file, err := os.Open("container/maze/maze.")
	if err != nil {
		panic(err)
	}

	maze := readMze(file)
	r, c := len(maze), len(maze[0])
	steps := make([][]int, r)

}
//node.go
type treeNode struct {
	value       int
	left, right *treeNode
}

//为结构体定义方法
func (node treeNode) print() {
	fmt.Print(node.value, " ")
}

//为结构体定义方法
func (node *treeNode) setValue(value int) {
	if node == nil {
		fmt.Println("setting value to nil node. Ignored.")
		return
	}
	node.value = value
}

/*
func (node *treeNode) traverse() {
	if node == nil {
		return
	}
	node.left.traverse()
	node.print()
	node.right.traverse()
}
*/

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 CreateNode(value int) *treeNode {
	return &treeNode{value: value}
}

func main() {
	var root treeNodee
	root = treeNode{value: 3}
	root.left = &treeNode{}
	root.right = &treeNode{5, nil, nil}
	root.right.left = new(treeNode)
	root.left.right = createNode(2) 
	root.right.left.setValue(4)

	root.traverse()
	
	root.right.left.print()
	fmt.Println()

	root.print()
	root.setValue(100)

	var pRoot *treeNode
	pRoot.SetValue(200)
	pRoot = &root
	pRoot.print()
	pRoot.SetValue(300)
	pRoot.print()

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

}

包和封装

  1. 名字一般使用CamelCase(驼峰命名法)
  2. 首字母大写:public
  3. 首字母小写:private
  4. 每个目录一个包
  5. main包包含可执行入口
  6. 为结构定义的方法必须放在同一个包内
  7. 为结构定义的方法可以放在不同的文件

扩展已有类型

Go语言的依赖管理

  1. 依赖的概念:基于第三方库进行开发
  2. 依赖管理的三个阶段: GOPATH, GOVENDOR, go mod
  3. go mod
    (1) 由go命令统一管理,用户不必关心目录结构
    (2) 初始化:go mod init
    (3) 增加依赖:go get
    (4) 更新依赖:go get [@v…], go mod tidy
    (5) 将旧项目迁移到go mod: go mod init, go build ./…

接口

  1. duck typing
    (1) “像鸭子走路,像鸭子叫 (长得像鸭子), 那么就是鸭子”
    (2) 描述事物的外部行为而非内部结构
    (3) 严格说go属于结构化类型系统,类似duck typing
  2. 接口的实现
    (1) 接口的实现是隐式的
    (2) 只要实现接口里的方法
  3. 接口变量里有什么
    (1) 实现者的类型
    (2) 实现者的值或者实现者的指针(指向实现者)
    PS:
    (1) 接口变量自带指针
    (2) 接口变量同样采用值传递,几乎不需要使用接口的指针
    (3) 指针接收者实现只能以指针方式使用;值接收者都可
  4. 查看接口变量
    (1) 表示任何类型:interface{}
    (2) Type Assertion
    (3) Type Switch
  5. 接口的组合
type ReadWriter interface {
	Reader
	Writer
}
  1. 特殊接口
    (1) Stringer
    (2) Reader/Writer
//retriever.go
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)
}
//mockretriever.go
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
}
//main.go
package main

import (
    "fmt"
    "mock"
    "real"
    "time"
)
type Retriever interface {
    Get(url string) string
}

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

const url = "http://www.immoc.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 immoc.com",
    })
    return s.Get(url)
}



func main() {
    var r Retriever

    retriever := mock.Retriever{
        "this is a fake imooc.com"}
    r = &retriever
    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("not a mock retriever")
    }

    fmt.Println("Try a session")
    fmt.Println(session(&retriever))
}

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

函数式编程

函数式编程 VS 函数指针

  1. 函数是一等公民:参数,变量返回值都可以是函数
  2. 高阶函数
  3. 函数–>闭包

“正统”函数式编程

  1. 不可变性:不能有状态,只有常量和函数
  2. 函数只能有一个参数
  3. 本课程不作上述严格规定

go语言闭包的应用

  1. 更为自然,不需要修饰如何访问自由变量
  2. 没有Lambda表达式,但是有匿名函数

资源管理与出错处理

defer调用

  1. 确保调用在函数结束时发生
  2. 参数在defer语句时计算
  3. defer列表为后进先出
//defer.go
package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
    "strings"
)

func Fibonacci() IntGen {
    a, b := 0, 1
    return func() int {
        a, b = b, a + b
        return a
    }
}

type IntGen func() int

func (g IntGen) Read(
    p []byte) (n int, err error) {
    next := g()
    if next > 10000 {
        return 0 , io.EOF
    }
    s := fmt.Sprintf("%d\n", next)

    // TODO: incorrect if p is too small!

    return strings.NewReader(s).Read(p)
}

func tryDefer() {
    //defer的调用顺序是先进后出
    // 3 2 1
    defer fmt.Println(1)
    defer fmt.Println(2)
    fmt.Println(3)
    panic("error occurred")
}

func tryDefer2() {
    for i := 0; i < 100; i++ {
        defer fmt.Println(i)
        if i == 30 {
            panic("printed too many")
        }
    }
}

func writeFile(filename string) {
    file, err := os.Create(filename)
    if err != nil {
        panic(err)
    }
    defer file.Close()

    writer := bufio.NewWriter(file)
    defer writer.Flush()

    f := Fibonacci()
    for i := 0; i < 20; i++ {
        fmt.Fprintln(writer, f())
    }
}

func main() {
    //tryDefer()
    writeFile("fibonacci.txt")
}

何时使用defer调用

  1. Open/Close
  2. Lock/Unlock
  3. PrintHeader/PrintFooter

错误处理

//web.go
package main

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

func main() {
    http.HandleFunc("/list/",
        func(writer http.ResponseWriter,
            request *http.Request) {
            path := request.URL.Path[len("/list/"):]  // /list/fib.txt
            file, err := os.Open(path)
            if err != nil {
                http.Error(writer,
                    err.Error(),
                    http.StatusInternalServerError)
                return
            }
            defer file.Close()

            all, err := ioutil.ReadAll(file)
            if err != nil {
                panic(err)
            }

            writer.Write(all)
        })

    err := http.ListenAndServe(":8888", nil)
    if err != nil {
        panic(err)
    }

}

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

居中的图片: Alt

居中并且带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block
int foo = 'bar';

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目Value
电脑$1600
手机$12
导管$1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列第二列第三列
第一列文本居中第二列文本居右第三列文本居左

SmartyPants

SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

|----------------|-------------------------------|---------------------------
|Single backticks|'Isn't this fun?' |‘Isn’t this fun?’
|Quotes |"Isn't this fun?" |“Isn’t this fun?”
|Dashes |-- is en-dash, --- is em-dash|-- is en-dash, — is emdash|

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

Mon 06 Mon 13 Mon 20 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
  • 关于 甘特图 语法,参考 这儿,

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart的流程图:

Created with Raphaël 2.2.0 开始 我的操作 确认? 结束 yes no
  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. [mermaid语法说明] ↩︎

  2. 注脚的解释 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值