go语言基础笔记---imooc
1、导学





goland插件:file watcher 添加

可以在保存后自动格式化代码


2、 go基础语法
1. 变量
package main
import "fmt"
var aa = 3 //作用于为包内,go没有全局变量的概念
var ss = "kkk" //函数外面也可以定义变量,但是必须使用var,不可以使用冒号等号
var ( //批量定义方式
aaa = 3
bbb = 4
ccc = true
)
func variableZeroValue() {
var a int
var s string
fmt.Printf("%d %q\n", a, s) //%q可以将字符串的引号打出来
}
func variableInitValue() {
var a, b int = 3, 4
var s string = "abc"
fmt.Println(a, b, s) //定义的变量一定要用到,不然会报错
}
func variableTypeDeduction() { //自动推断变量类型
var a, b, c, s = 3, 4, true, "def"
fmt.Println(a, b, c, s)
}
func variableShorter() {
a, b, c, s := 3, 4, true, "def" //推荐定义方式
b = 5 //再次赋值不可以用冒号
fmt.Println(a, b, c, s)
}
func main() {
fmt.Println("hello world")
variableZeroValue()
variableInitValue()
variableTypeDeduction()
variableShorter()
fmt.Println(aaa, bbb, ccc)
}
输出
hello world
0 ""
3 4 abc
3 4 true def
3 5 true def
3 4 true
注意
:=只能在函数内部使用,在函数外只能使用var定义变量
2. 内建变量类型
bool
string
uint //长度取决于操作系统的位数
// (u)int,(u)int8,(u)int16,(u)int32,(u)int64
uintptr //指针
byte //8位
rune //字符型,32位,相当于其他的语言的char
float32 // float64 需要明确指出float和complex的位数
complex64 //complex128 ,复数
在多国语言的环境下,一个字节的char会造成很多麻烦,所以go语言采用了4字节的rune代表字符
func euler() {
c := 3 + 4i
fmt.Println(cmplx.Abs(c))
fmt.Println(cmplx.Pow(math.E, 1i*math.Pi) + 1)
fmt.Printf("%.3f", cmplx.Exp(1i*math.Pi)+1)
}
结果
5
(0+1.2246467991473515e-16i)
(0.000+0.000i)
强制类型转换
- 类型转换是强制的,go没有隐式类型转换
func triangle() {
var a, b int = 3, 4
var c int
//错误写法:c = math.Sqrt(a*a + b*b)
c = int(math.Sqrt(float64(a*a + b*b)))
fmt.Println(c)
}
func Sqrt(x float64) float64 {
if haveArchSqrt {
return archSqrt(x)
}
return sqrt(x)
}
Sqrt需要的是float64类型的参数,因此要进行强制类型转换,返回值类型是float64,也要强制转换为int
3. 常量和枚举
常量
const filename = "abc.txt"
func consts() {
const a, b = 3, 4
var c int
c = int(math.Sqrt(a*a + b*b))
fmt.Println(filename, c)
}
如果定义常量时不指定类型,那么常量的数值可以作为各种类型使用,因此sqrt内部不需要进行类型转换。
一般go语言中常量不大写
枚举
func enums() {
const (
cpp = iota
_
goland
python
javascript
)
//b, kb, mb, gb, tb, pb
const (
b = 1 << (10 * iota)
kb
mb
gb
tb
pb
)
fmt.Println(cpp, javascript, goland, python) //0 4 2 3
fmt.Println(b, kb, mb, gb, tb, pb) //1 1024 1048576 1073741824 1099511627776 1125899906842624
}
iota表示自动编号,可以参与运算
变量定义要点回顾
- 变量类型写在变量名之后
- 编译器可以推测变量类型
- 没有char,只有rune
- 原生支持复数类型
4.条件语句
if: 条件不需要加括号
func test01() {
const filename = "abc.txt"
contents, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s\n", contents)
}
}
//第二种写法
func test02() {
const filename = "abc.txt"
if contents, err := ioutil.ReadFile(filename); err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s\n", contents)
}
}
- if的条件里可以赋值
- 条件里赋值的变量的作用域仅限于这个if语句内部
switch:自动break,除非使用fallthrough
//swich后可以不带表达式
func grade(score int) string {
g := ""
switch {
case score < 0 || score > 100:
panic(fmt.Sprintf("Wrong score: %d", score))
case score < 60:
g = "D"
case score < 80:
g = "C"
case score < 90:
g = "B"
case score <= 100:
g = "A"
}
return g
}
5. 循环
for条件里不需要括号,条件里可以省略初始条件,结束条件,递增表达式
//十进制转二进制:除以2反序取余
func convertToBinary(n int) string {
if n == 0 {
return strconv.Itoa(0)
}
result := ""
for ; n > 0; n /= 2 {
lsb := n % 2 //取余
result = strconv.Itoa(lsb) + result //反序
}
return result
}
func printFile(filename string) {
file, err := os.Open(filename)
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(file)
for scanner.Scan() { //相当于while
fmt.Println(scanner.Text())
}
}
goland快捷键:自动生成函数返回值变量名:ctrl+shift+v
基本语法要点
- for , if 后面条件没有括号
- if条件里也可以定义变量
- 没有while
- switch不需要break,也可以直接switch多个条件
6. 函数
func eval(a, b int, op string) (int, error) {
switch op {
case "+":
return a + b, nil
case "-":
return a - b, nil
case "*":
return a * b, nil
case "/":
//return a / b
q, _ := div1(a, b)
return q, nil
default:
return 0, fmt.Errorf("unsupported operation: %s", op)
}
}
// 13 / 3 = 4.... 1
func div(a, b int) (int, int) {
return a / b, a % b
}
func div1(a, b int) (q, r int) { //返回值起别名
q = a / b
r = a % b
return
}
可以将函数当做形参传入函数内
func apply(op func(int, int) int, a, b int) int {
pointer := reflect.ValueOf(op).Pointer()
opName := runtime.FuncForPC(pointer).Name()
fmt.Printf("Calling function %s with args "+
"(%d, %d)\n", opName, a, b)
return op(a, b)
}
func pow(a, b int) int {
return int(math.Pow(float64(a), float64(b)))
}
func main() {
fmt.Println(apply(pow, 3, 2))
}
输出
Calling function main.pow with args (3, 2)
9
可变参数列表
func sum(numbers ...int) int {
sum := 0
for i := range numbers {
sum += numbers[i]
}
return sum
}
func main() {
fmt.Println(sum(1, 2, 3, 4, 5, 6, 7, 8, 9))
}
函数语法要点
- 返回值类型写在最后
- 可以返回多个值
- 函数作为参数
- 没有默认参数、可选参数
- 有可变参数列表
7. 指针
var a int = 3
var pa *int = &a
*pa = 5
指针不能运算
go语言只有值传递这一种方式
可以用指针来实现引用传递
func swap(a, b *int) {
*b, *a = *a, *b
}
func swap1(a, b int) (int, int) {
return b, a
}
func main() {
a, b := 3, 4
swap(&a, &b)
fmt.Println(a, b) //4 3
c, d := 5, 6
c, d = swap1(c, d)
fmt.Println(c, d) //6 5
}
8. 数组
数组定义:
var arr1 [5]int
arr2 := [3]int{1, 3, 4}
arr3 := [...]int{2, 4, 5, 6, 7} //让编译器自动判断有多少数据,需要使用...
var grid [4][5]int //4个长度为5的数组
//[[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]
数组遍历:
for i := 0; i < len(arr3); i++ {
fmt.Println(arr3[i])
}
for i, v := range arr3 {
fmt.Println(i, v)
}
for _, v := range arr3 {
fmt.Println(v)
}
- 使用下划线_来省略遍历
- 类似于java中的:for each
数组是值类型
func printArray(arr [5]int) {
arr[0] = 100
for i, v := range arr {
fmt.Println(i, v)
}
}
func main() {
arr3 := [...]int{2, 4, 6, 8, 10}
fmt.Println("Modified arr3:")
printArray(arr3)
fmt.Println("arr3:")
fmt.Println(arr3)
}
/* Result:
Modified arr3:
0 100
1 4
2 6
3 8
4 10
arr3:
[2 4 6 8 10]
*/
[10]int和[20]int是不同的类型- 调用
func f(arr [10]int)会拷贝数组,与java不同
可以传入数组的引用
func printArray2(arr *[5]int) {
arr[0] = 100
for i, v := range arr {
fmt.Println(i, v)
}
}
func main() {
arr3 := [...]int{2, 4, 6, 8, 10}
fmt.Println("==============")
fmt.Println("Modified arr3:")
printArray2(&arr3) //注意需要使用引用&
fmt.Println("arr3:")
fmt.Println(arr3)
}
/*Result
Modified arr3:
0 100
1 4
2 6
3 8
4 10
arr3:
[100 4 6 8 10]
*/
- go语言中一般不直接使用数组
9. 切片Slice
func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8}
s := arr[2:6] //左闭右开
s1 := arr[:6]
s2 := arr[2:]
s3 := arr[:]
fmt.Println("arr[2:6] =", s)
fmt.Println("arr[:6] =", s1)
fmt.Println("arr[2:] =", s2)
fmt.Println("arr[:] =", s3)
}
/*
arr[2:6] = [2 3 4 5]
arr[:6] = [0 1 2 3 4 5]
arr[2:] = [2 3 4 5 6 7 8]
arr[:] = [0 1 2 3 4 5 6 7 8]
*/
Slice是数组的视图
func update(arr []int) {
arr[1] = 100
}
func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8}
s1 := arr[:6]
fmt.Println("s1: ", s1)
fmt.Println("After update")
update(s1)
fmt.Println("s1: ", s1)
fmt.Println("arr:", arr)
}
/*
s1: [0 1 2 3 4 5]
After update
s1: [0 100 2 3 4 5]
arr: [0 100 2 3 4 5 6 7 8]
*/
使用数组的方法:切片
func printArray3(arr []int) {
arr[0] = 100
for _, v := range arr {
fmt.Print(" ", v)
}
}
printArray3(arr3[:])
Reslice
func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8}
s2 := arr[2:]
fmt.Println("Reslice")
fmt.Println(s2)
s2 = s2[:5]
fmt.Println(s2)
s2 = s2[2:]
fmt.Println(s2)
}
/*
Reslice
[2 3 4 5 6 7 8]
[2 3 4 5 6]
[4 5 6]
*/
Slice扩展
问题1:
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 = arr[2:6]
s2 = s1[3:5]
s1 = ?
s2 = ?
-----------
s1= [2 3 4 5]
s2= [5 6]
- slice可以向后扩展,不可以向前扩展
- s[i]不可以超越len(s),向后扩展不可以超越底层数组cap(s)
问题2:
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s2 = s1[3:5]
s3 = append(s2, 10)
s4 := append(s3, 11)
s5 := append(s4, 12)
s3 = ?
s4 = ?
s5 = ?
arr = ?
-------
[5 6 10] [5 6 10 11] [5 6 10 11 12]
[0 1 2 3 4 5 6 10]
- 添加元素是如果超越cap,系统会重新分配更大的底层数组,原数组若无人使用则会被自动回收
- 由于值传递的关系,必须接收append的返回值
- s = append(s, val)
切片的创建
var s []int;
s1 := []int{2,4,6,8}
s2 := make([]int, 16)
s3 := make([]int, 16, 32)//长度为16,容量为32的切片
- 切片容量的扩容按照2的倍数递增
切片内容的复制、删除
func printSlice(s []int) {
fmt.Printf("%v, len=%d, cap=%d\n", s, len(s), cap(s))
}
func main() {
var s []int
s1 := []int{2, 4, 6, 8}
s2 := make([]int, 16)
s3 := make([]int, 16, 32) //长度为16,容量为32的切片
fmt.Println(s, s1, s2, s3)
fmt.Println("Coping elements from slices")
copy(s2, s1)
printSlice(s2)
fmt.Println("Deleting elements from slices")
s2 = append(s2[:3], s2[4:]...)
printSlice(s2)
fmt.Println("Poping from front")
front := s2[0]
s2 = s2[1:]
fmt.Println(front)
printSlice(s2)
fmt.Println("Poping from tail")
tail := s2[len(s2)-1]
s2 = s2[:len(s2)-1]
fmt.Println(tail)
printSlice(s2)
}
-----------
[] [2 4 6 8] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
Coping elements from slices
[2 4 6 8 0 0 0 0 0 0 0 0 0 0 0 0], len=16, cap=16
Deleting elements from slices
[2 4 6 0 0 0 0 0 0 0 0 0 0 0 0], len=15, cap=16
Poping from front
2
[4 6 0 0 0 0 0 0 0 0 0 0 0 0], len=14, cap=15
Poping from tail
0
[4 6 0 0 0 0 0 0 0 0 0 0 0], len=13, cap=15
10. Map
创建:
m := map[string]string{
"name": "gwf",
"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)
----------output:-------------
map[course:golang name:gwf quality:notbad site:imooc] map[] map[]
读取元素:
-
key不存在时,获得value的nil
-
用
value,ok:= m[key]来判断是否存在key
courseName, ok := m["course"]
fmt.Println(courseName, ok)
if courseName1, ok := m["corse"]; ok {
fmt.Println(courseName1, ok)
} else {
fmt.Println("key does not exist")
}
----------output-----------
golang true
key does not exist
删除元素:
name, ok := m["name"]
fmt.Println(name, ok)
delete(m, "name")
name, ok = m["name"]
fmt.Println(name, ok)
---------output------------
gwf true
false
遍历
- 使用range遍历key,或者遍历key,value对
- 不保证遍历顺序,如需顺序,需要手动对key进行排序
map中的key
- map使用hash表,必须可以比较相等
- 除了slice,map,function的内建类型都可以作为key
- struct类型不包含上述字段,也可作为key
例题:寻找最长不含有重复字符的子串
剑指 Offer 48. 最长不含重复字符的子字符串 - 力扣(LeetCode)
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
提示:
s.length <= 40000
思路
对于每一个字母x,
- lastOccurred[x]不存在,或者小与start --->无需操作
- lastOccurred[x]大于等于start --->更新start
- 更新lastOccurred[x],更新maxLenth
func lengthOfNonRepeatingSubstr(s string) int {
lastOccurred := make(map[byte]int)
maxLength := 0
start := 0
for i, ch := range []byte(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
}
11. 字符和字符串的处理rune
func main() {
s := "yes我爱慕课网!"
fmt.Println(s)
for _, ch := range []byte(s) {
fmt.Printf("%X ", ch)
}
fmt.Println()
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)
}
}
yes我爱慕课网!
79 65 73 E6 88 91 E7 88 B1 E6 85 95 E8 AF BE E7 BD 91 21
(0 79) (1 65) (2 73) (3 6211) (6 7231) (9 6155) (12 8BFE) (15 7F51) (18 21)
Rune count: 9
y e s 我 爱 慕 课 网 !
(0 y) (1 e) (2 s) (3 我) (4 爱) (5 慕) (6 课) (7 网) (8 !)
- 使用range遍历pos,rune对
- 使用utf8.RuneCountInString(s)获得字符串s的字符数量
- 使用len获得字节长度
- 使用[]byte获得全部字节
例题:寻找最长不含有重复字符的子串
国际版:支持所有字符
func lengthOfNonRepeatingSubstr(s string) int {
lastOccurred := make(map[rune]int)
maxLength := 0
start := 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
}
将byte换为rune即可
12. 面向对象--结构体
- go语言仅支持封装,不支持继承和多态
- go语言没有class,只有struct
type treeNode struct {
value int
left, right *treeNode
}
func main() {
var root treeNode
root = treeNode{value: 3}
root.left = &treeNode{}
root.right = &treeNode{5, nil, nil}
root.right.left = new(treeNode)
nodes := []treeNode{
{value: 3},
{},
{6, nil, &root},
}
fmt.Println(nodes)
}
root.right.left = new(treeNode):不论是地址还是结构本身,一律使用.来访问成员
- go没有构造函数,可以用自定义工厂函数代替,如
func creatNode(value int) *treeNode {
return &treeNode{value: value} //与cpp不同,go语言可以向外界返回局部变量
}
- 注意:返回了局部变量的地址
**结构创建在堆上还是栈上? **
- 不需要知道
结构体方法
//结构体方法
func (node treeNode) print() { //node传递的是值
fmt.Print(node.value)
}
//使用
node.print()
node传递的是值
func (node treeNode) setValue(value int) { //值传递
node.value = value
}
func (node *treeNode) setValue1(value int) { //引用传递
node.value = value
}
root.right.left = new(treeNode)
root.right.left.setValue(4)
root.right.left.print() // 0
root.right.left.setValue1(4)
root.right.left.print() // 4
编译器会自动确定传入的是地址还是值
- 只有指针才能改变结构体内容
- nil指针也可以调用方法
func (node *treeNode) setValue1(value int) {
if node == nil {
fmt.Println(" Setting value to nil. Ignored.")
return
}
node.value = value
}
var proot *treeNode
proot.setValue1(300)
proot = &root //root非空
proot.setValue1(500)
proot.print()
---------
Setting value to nil. Ignored.
500
中序遍历
//中序遍历
func (node *treeNode) traverse() {
if node == nil {
return
}
//go支持nil调用方法,因此不必判断node.left != nil
node.left.traverse()
node.print()
node.right.traverse()
}
值接收者 VS 指针接受者
- 要改变内容必须使用指针接收者
- 结构过大考虑使用指针接收者
- 一致性:如果有指针接收者,最好都是指针接收者
- 值接收者是go语言特有
- 值/指针接收者均可接收值/指针
13. 封装
- 名字一般使用CamelCase
- 首字母大写代表public,首字母小写代表private --->针对包而言
包
- 每个目录一个包
- 包名与目录名不必相同
- main包包含可执行入口
- 为结构体定义的方法必须放在同一个包内,可以是不同的文件
如何扩充系统类型或者别人的类型
- 定义别名:最简单
- 使用组合:最常用
- 使用内嵌:可以省下很多代码
组合
type myTreeNode struct {
node *tree.Node
}
func (myNode *myTreeNode) postOrder() {
if myNode == nil || myNode.node == nil {
return
}
left := myTreeNode{myNode.node.Left}
left.postOrder()
right := myTreeNode{myNode.node.Right}
right.postOrder()
myNode.node.Print()
}
myRoot := myTreeNode{&root}
myRoot.postOrder()
别名
type Queue []int
func (q *Queue) Push(v int) {
*q = append(*q, v)
}
func (q *Queue) Pop() int {
head := (*q)[0]
*q = (*q)[1:]
return head
}
func (q *Queue) IsEmpty() bool {
return len(*q) == 0
}
func main() {
q := queue.Queue{1, 2}
q.Push(3)
q.Push(4)
fmt.Println(q.Pop())
fmt.Println(q.Pop())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
//初始的q和后面的q不是同一个q,push和pop操作改变了指针
}
使用内嵌的方式来扩展已有的类型
type myTreeNode struct {
*tree.Node //Embedding
}
func (myNode *myTreeNode) postOrder() {
if myNode == nil || myNode.Node == nil {
return
}
left := myTreeNode{myNode.Left}
left.postOrder()
right := myTreeNode{myNode.Right}
right.postOrder()
myNode.Print()
}
func main() {
root := myTreeNode{&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.CreatNode(2)
root.Right.Left.SetValue1(4)
root.postOrder()
}
相当于把tree.Node的内容全都拉过来了
与java的继承的区别
type myTreeNode struct {
*tree.Node //Embedding
}
func (node *myTreeNode) Traverse() {
fmt.Println("This is shadowed.")
}
func (myNode *myTreeNode) postOrder() {
if myNode == nil || myNode.Node == nil {
return
}
left := myTreeNode{myNode.Left}
left.postOrder()
right := myTreeNode{myNode.Right}
right.postOrder()
myNode.Print()
}
func main() {
root := myTreeNode{&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.CreatNode(2)
root.Right.Left.SetValue1(4)
fmt.Println("In-order traversal")
fmt.Print("root.Traverse(): ")
root.Traverse()
fmt.Print("root.Node.Traverse(): ")
root.Node.Traverse()
fmt.Println()
fmt.Println("post-order traversal")
root.postOrder()
}
In-order traversal
root.Traverse(): This is shadowed.
root.Node.Traverse(): 0 2 3 4 5
post-order traversal
2 0 4 5 3
- 可以定义与内嵌对象的函数重名的函数:shadowed
- 子类不能赋值给父类

浙公网安备 33010602011771号