这里写自定义目录标题
Basics
- Go语言指针不能进行运算。
- Go语言只有值传递一种方式。
- 函数式编程(可以将函数作为参数传入函数)例子:
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))
}
- go语言可变参数例子:
func sum(numbers ...int) int {
s := 0
for i:= range numbers {
s += numbers[i]
}
return s
}
- go语言只可以调用指针,不可以对指针进行运算:
package main
func main() {
var a int = 2
var pa *int = &a
*pa = 3
fmt.Println(a)
}
切片(Slice)
- slice是array的view(视图)
- slice可以向后扩展, 不可以向前扩展
- s[i]不可以超过len(s), 向后扩展不可以超越底层数组cap(s)
- 添加元素时如果超越cap, 系统会重新分配更大的底层数组
- 由于值传递的关系, 必须接受append的返回值
- 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
- 创建map: make(map[string]int)
- 获取元素: m[key]
- key不存在时,获得value类型的初始值
- 用value, ok :=m[key]来判断是否存在key
- 用delete删除一个key
- 使用range遍历key,或者遍历key, value对
- 不保证遍历顺序,如需顺序,需手动对key排序
- 使用len获得元素个数
- map使用哈希表,必须可以比较相等
- 除了slice, map, function, 内建类型都可以作为key
- 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语言字符和字符串处理
- 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()
}
- 例:寻找最长不含有重复字符的子串
解:对于每一个字母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("一二三二一"))
}
结构体和方法
- go语言仅支持封装,不支持继承和多态
- go语言没有class, 只有struct
- 无论地址还是结构本身,一律使用 . 来访问成员
- go语言的函数中,局部变量的地址也可以作为函数返回值
- 为结构体定义方法时需注意,如果想改变结构内容,要使用指针作为方法接收者。
只有指针才可以改变结构内容。 - 结构过大也考虑使用指针接收者,因为指针接收者拷贝速度比值接收者快
- 一致性(建议):如有指针接收者,最好都是指针接收者
- 值接收者是go语言特有
- 值/指针接收者均可接收值/指针
- 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)
}
包和封装
- 名字一般使用CamelCase(驼峰命名法)
- 首字母大写:public
- 首字母小写:private
- 每个目录一个包
- main包包含可执行入口
- 为结构定义的方法必须放在同一个包内
- 为结构定义的方法可以放在不同的文件
扩展已有类型
Go语言的依赖管理
- 依赖的概念:基于第三方库进行开发
- 依赖管理的三个阶段: GOPATH, GOVENDOR, go mod
- 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 ./…
接口
- duck typing
(1) “像鸭子走路,像鸭子叫 (长得像鸭子), 那么就是鸭子”
(2) 描述事物的外部行为而非内部结构
(3) 严格说go属于结构化类型系统,类似duck typing - 接口的实现
(1) 接口的实现是隐式的
(2) 只要实现接口里的方法 - 接口变量里有什么
(1) 实现者的类型
(2) 实现者的值或者实现者的指针(指向实现者)
PS:
(1) 接口变量自带指针
(2) 接口变量同样采用值传递,几乎不需要使用接口的指针
(3) 指针接收者实现只能以指针方式使用;值接收者都可 - 查看接口变量
(1) 表示任何类型:interface{}
(2) Type Assertion
(3) Type Switch - 接口的组合
type ReadWriter interface {
Reader
Writer
}
- 特殊接口
(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 函数指针
- 函数是一等公民:参数,变量返回值都可以是函数
- 高阶函数
- 函数–>闭包
“正统”函数式编程
- 不可变性:不能有状态,只有常量和函数
- 函数只能有一个参数
- 本课程不作上述严格规定
go语言闭包的应用
- 更为自然,不需要修饰如何访问自由变量
- 没有Lambda表达式,但是有匿名函数
资源管理与出错处理
defer调用
- 确保调用在函数结束时发生
- 参数在defer语句时计算
- 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调用
- Open/Close
- Lock/Unlock
- 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编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
- 全新的界面设计,将会带来全新的写作体验;
- 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
- 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
- 全新的 KaTeX数学公式 语法;
- 增加了支持甘特图的mermaid语法1 功能;
- 增加了 多屏幕编辑 Markdown文章功能;
- 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
- 增加了 检查列表 功能。
功能快捷键
撤销: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.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.
// An highlighted block
int foo = 'bar';
生成一个适合你的列表
- 项目
- 项目
- 项目
- 项目
- 项目1
- 项目2
- 项目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)=(n−1)!∀n∈N 是通过欧拉积分
Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
- 关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:
这将产生一个流程图。:
- 关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
- 关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。