基础
导出名
函数
函数参数名,参数类型声明在形参名之后。包括返回参数类型也要放在 ‘函数名 ()’ 之后。
当多个函数形参参数类型一样,除最后一个参数类型,其他的参数类型声明可以删除。
多值返回
有点类似 Javascript 中解构那意思
关键点在:
- 函数中返回值声明多个形参
- 使用函数赋值
:=```go package main
import “fmt”
func swap(x, y string) (string, string) { return y, x }
func main() { a, b := swap(“hello”, “world”) fmt.Println(a, b) }
<a name="Ju5mp"></a>#### 命名返回值- Go 的返回值可被命名,它们会被视作定义在函数顶部的变量。- 返回值的名称应当具有一定的意义,它可以作为文档使用。- 没有参数的 `return` 语句返回已命名的返回值。也就是 `直接` 返回。- 直接返回语句应当仅用在下面这样的短函数中。在长的函数中它们会影响代码的可读性。```gopackage mainimport "fmt"func split(sum int) (x, y int) {x = sum * 4 / 9y = sum - xreturn}func main() {fmt.Println(split(17))}
变量初始值
- 若变量已赋值,可以自动从初始值获取变量类型
var i, j int = 1, 2
短变量声明(:=)
- 在函数中,简洁赋值语句
:=可在类型明确的地方代替var声明。 函数外的每个语句都必须以关键字开始(
var,func等等),因此:=结构不能在函数外使用。常量
-
for
for 语句后面的三个构成部分外没有小括号, 大括号
{ }则是必须的。- 初始化语句和后置语句是可选的。
- 可以去掉分号,因为 C 的
while在 Go 中叫做for。 ```go package main
import “fmt”
func main() { sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum) // 45
// --- 省略初始化和后置语句 ---sum = 1for ; sum < 1000; {sum += sum}fmt.Println(sum) // 1024// --- while ---sum = 1for sum < 1000 {sum += sum}fmt.Println(sum) // 1024// --- 无限循环 ---// for { }
}
<a name="IuMU7"></a>#### if- Go 的 `if` 语句与 `for` 循环类似,表达式外无需小括号 `( )` ,而大括号 `{ }` 则是必须的。```gopackage mainimport "fmt"func main() {x := 1if x < 0 {fmt.Println("x is less 0")} else {fmt.Println("x is greater than 0")}// x is greater than 0}
- 同
for一样,if语句可以在条件表达式前执行一个简单的语句。该语句声明的变量作用域仅在if之内。 ```go package main
import ( “fmt” “math” )
func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } // fmt.Println(v) // undefined: v return lim }
func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) }
```gopackage mainimport ("fmt""math")func pow(x, n, lim float64) float64 {if v := math.Pow(x, n); v < lim {return v} else {fmt.Printf("%g >= %g\n", v, lim)}// 这里开始就不能使用 v 了return lim}func main() {fmt.Println(pow(3, 2, 10),pow(3, 3, 20),)}
// 练习1:sqrt函数package mainimport ("fmt""math")// Sqrt is a functionfunc Sqrt(x float64) float64 {z := float64(1)for {if math.Abs(z*z-x) < 0.00000000000001 {break}z -= (z*z - x) / (2 * z)fmt.Println(z, z*z)}return z}func main() {fmt.Println("return", Sqrt(2))}
switch
- 没有条件的 switch 同
switch true一样。
指针
package mainimport "fmt"func main() {i, j := 42, 2701p := &i // 指向 ifmt.Println("p :", p)fmt.Println("*p :",*p) // 通过指针读取 i 的值fmt.Println()*p = 21 // 通过指针设置 i 的值fmt.Println("i :", i) // 查看 i 的值fmt.Println("p :", p)fmt.Println("*p :",*p)fmt.Println()p = &j // 指向 j*p = *p / 37 // 通过指针对 j 进行除法运算fmt.Println("j :", j) // 查看 j 的值fmt.Println("p :", p)fmt.Println("*p :",*p)}
结构体
package mainimport "fmt"type Vertex struct {X intY int}func main() {fmt.Println(Vertex{1, 2}) // {1 2}v := Vertex{1, 2}fmt.Println(v.X) // 1v.X = 4fmt.Println(v.X) // 4}
结构体指针
- 如果我们有一个指向结构体的指针
p,那么可以通过(*p).X来访问其字段X。不过这么写太啰嗦了,所以语言也允许我们使用隐式间接引用,直接写p.X就可以。 ```go package main
import “fmt”
type Vertex struct { X int Y int }
func main() { v := Vertex{1, 2} p := &v p.X = 1e9 fmt.Println(v) }
<a name="R01bz"></a>#### 结构体文法- 使用 `Name:` 语法可以仅列出部分字段。(字段名的顺序无关。)- 特殊的前缀 `&` 返回一个指向结构体的指针。```gopackage mainimport "fmt"type Vertex struct {X, Y int}var (v1 = Vertex{1, 2} // 创建一个 Vertex 类型的结构体v2 = Vertex{X: 1} // Y:0 被隐式地赋予v3 = Vertex{} // X:0 Y:0p = &Vertex{1, 2} // 创建一个 *Vertex 类型的结构体(指针))func main() {fmt.Println(v1) // {1 2}fmt.Println(v2) // {1 0}fmt.Println(v3) // {0 0}fmt.Println(p) // &{1 2}fmt.Println(*p) // {1 2}}
数组
- 类型
[n]T表示拥有n个T类型的值的数组。 - 数组不能改变大小 ```go var a [2]string
primes := [6]int{2, 3, 5, 7, 11, 13}
<a name="DnDRG"></a>#### 切片- 切片通过两个下标来界定,即一个上界和一个下界,二者以冒号分隔:a[low : high]- 半开区间,包括第一个元素,但排除最后一个元素。- 切片下界的默认值为 `0`,上界则是该切片的长度。```gopackage mainimport "fmt"func main() {primes := [6]int{2, 3, 5, 7, 11, 13}var s []int = primes[1:4]fmt.Println(s) // [3 5 7]}
可以把切片类比为js中的引用,是一种浅拷贝,更改切片会变更初始数组
切片就像数组的引用
- 切片并不存储任何数据,它只是描述了底层数组中的一段。
- 更改切片的元素会修改其底层数组中对应的元素。
- 与它共享底层数组的切片都会观测到这些修改。
package mainimport "fmt"func main() {names := [4]string{"John","Paul","George","Ringo",}fmt.Println(names) // [John Paul George Ringo]a := names[0:2]b := names[1:3]fmt.Println(a, b) // [John Paul] [Paul George]b[0] = "XXX"fmt.Println(a, b) // [John XXX] [XXX George]fmt.Println(names) // [John XXX George Ringo]s := []struct {i intb bool}{{2, true},{3, false},{5, true},{7, true},{11, false},{13, true},}fmt.Println(s) // [{2 true} {3 false} {5 true} {7 true} {11 false} {13 true}]}
切片容量
package mainimport "fmt"func main() {s := []int{2, 3, 5, 7, 11, 13}printSlice(s)// 截取切片使其长度为 0s = s[:0]printSlice(s)// 拓展其长度s = s[:4]printSlice(s)// 舍弃前两个值s = s[2:]printSlice(s)/*len=6 cap=6 [2 3 5 7 11 13]len=0 cap=6 []len=4 cap=6 [2 3 5 7]len=2 cap=4 [5 7]*/}func printSlice(s []int) {fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)}

make创建动态数组
package mainimport "fmt"func main() {a := make([]int, 5)printSlice("a", a)b := make([]int, 0, 5)printSlice("b", b)c := b[:2]printSlice("c", c)d := c[2:5]printSlice("d", d)// len=5 cap=5 [0 0 0 0 0]// b len=0 cap=5 []// c len=2 cap=5 [0 0]// d len=3 cap=3 [0 0 0]}func printSlice(s string, x []int) {fmt.Printf("%s len=%d cap=%d %v\n",s, len(x), cap(x), x)}
range
- 可以将下标或值赋予
_来忽略它。 - 只需要索引,忽略第二个变量即可。 ```go package main
import “fmt”
func main() { pow := make([]int, 10)
// 无需元素副本for i := range pow {pow[i] = 1 << uint(i) // == 2**i}// 使用 _ 代表忽略下标for _, value := range pow {fmt.Printf("%d\n", value)}
}
<a name="yOC8e"></a>#### 映射> 我一直觉得 “类比” 学习法是一个不可或缺的学习方法。可以类比“js”里的object- 在映射 `m` 中插入或修改元素:`m[key] = elem`- 获取元素:`elem = m[key]`- 删除元素:`delete(m, key)`- 通过双赋值检测某个键是否存在:`elem, ok = m[key]`- 若 `key` 在 `m` 中,`ok` 为 `true` ;否则,`ok` 为 `false`。- 若 `key` 不在映射中,那么 `elem` 是该映射元素类型的零值。- 同样的,当从映射中读取某个不存在的键时,结果是映射的元素类型的零值。> **注** :若 `elem` 或 `ok` 还未声明,你可以使用短变量声明:`elem, ok := m[key]`<a name="fOr8h"></a>#### 函数闭包go函数可以返回一个闭包```gopackage mainimport "fmt"func adder() func(int) int {sum := 0return func(x int) int {fmt.Println("x", x)sum += xreturn sum}}func main() {pos, neg := adder(), adder()for i := 0; i < 10; i++ {fmt.Println(pos(i),neg(-2*i),)}}
方法
package mainimport ("fmt""math")type Vertex struct {X, Y float64}func (v Vertex) Abs() float64 {return math.Sqrt(v.X*v.X + v.Y*v.Y)}func main() {v := Vertex{3, 4}fmt.Println(v.Abs())}
- 方法就是一类带特殊的 接收者 参数的函数。
- 方法接收者在它自己的参数列表内,位于
func关键字和方法名之间。 - 在此例中,
Abs方法拥有一个名为v,类型为Vertex的接收者。
