Go的变量与函数声明方式
//函数调用方法
a,b := swap(...)
//函数编写格式
func name(x, y string) (int, string){
do something...
}
func name(x, y string) (c1 int, c2 string){
return //此时return会返回 c1和c2
}
//变量声明方法
var i int
var i, j int = 1, 2
var i, f, s = 1, 1.23, "hello"
i, f, s := 1, 1.23, "hello"
//基本类型
bool, string, int, uint, float32, float64, complex64, complex 128
//!不同类型需要显式类型转换
//常量命名
const Pi = 3.1415926
Go的for循环,if, switch, defer语句
//for循环格式
for i:=0; i<10; i++{
fmt.println(i)
}
//for循环缺省格式
for ;sum<1000;{
sum+=sum
}
//Go的for循环缺省格式可以去掉‘;’,可以看作Go语言的while循环
sum := 1
for sum<1000{
sum += sum
}
//Go的无限循环
for{
do something
}
//if格式
if x<0{
do something....
}
//if语句可在条件表达式前执行一个简单语句,该语句声明的变量作用域仅在if内
if v:=math.Pow(x,n); x<lim{
do someting//v只能在次作用域内使用
}
//switch只能用于特定的Case,且每个分支后自动添加break语句
switch os:=runtime.GOOS; os{
case "darwin":
do something
case "linux":
do something
default:
do something
}
//没有条件的switch相当于switch true
//此种方法可以逻辑更清晰的表示一长串if...else...语句
switch{
case t.Hour()<12:
do something
case t.Hour()<17:
do something
default:
do something
}
//defer语句将函数推迟到外层函数返回之后执行,推迟调用的函数的参数立刻求值,但知道外层函数返回前,该函数都不会被调用
func main(){
i := 5
defer fmt.Println(i)
i++
fmt.Println("hello") //输出5
}
//!有多个推迟函数时,推迟函数会被依次压入一个栈中,外层函数返回后按照后进先出的顺序执行
//go语言指针与C语言相似
var p *int
i := 5
p = &i
*p = 12
fmt.Println(i)
Go结构体,数组,切片
//Go结构体定义
type Vertex struct{
X int
Y int
}
var(
v1 = Vertex{1,2}
v2 = Vertex{X:1} //Y=0
v3 = Vertex{} //X=0, Y=0
p = &Vertex{1,2}
)
func main(){
fmt.Println(v1, (*p).X, v2, v3)
}
//Go数组定义
var a [2]string
primes := [6]int{2,3,4,5,6,7}
var s []int = primes[1:4] //[1,4)切片,其实相当于C语言里的数组指针
//切片并不储存任何数据,只相当于一个试图
//更改切片数据会修改底层数组中对应的元素,与它共享底层数据的切片也会随之变化
s:=[]struct{
i int
b bool
}{
{2, true}
{3, true}
{5, false}
}
//切片长度
len(s)
//切片容量
//切片容量是从切片的第一个元素开始数,到底层数组元素末尾的个数
cap(s)
//切片文法与数组文法不同
[3] bool{true, true, false} //数组文法
[] bool{true, true, false} //切片文法
//上述切片文法其实相当于构建了一个数组,然后构建一个引用它的切片
func printSlice(s []int)只能传切片文法
//切片零值为nil
var s []int //s == nil
//make函数分配一个元素为零值的数组并返回一个引用了它的切片
a := make([]int, 5) //len(a)==5
a := make([]int, 0, 5) //len(a)==0, cap(a)==5
//切片中可以存切片,相当于c语言二维数组
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
}
//向切片追加元素
var s []int
s = append(s,0)
s = append(s,2,3,4)
Range
//for循环的range形式可以便利切片或映射
//当使用for循环遍历切片时,每次迭代会返回两个值,第一个为当前元素的下标,第二个值是所对应元素的一个副本
var pow = []int{1, 2, 4, 8}
func main(){
for i, v:= range pow{
fmt.Println(i,v)
}
}
//只需要key的情况
for i := range pow{
fmt.Println(i)
}
//只需要value的话,可以使用‘_‘来忽略它
for _, value := range pow{
fmt.Println(value)
}
映射
//Go的映射就是python的字典,c++的map
type Vertex struct{
Lat, Long float64
}
var m map[string] Vertex //定义一个映射,现在这个映射为nil,没有键,也不能添加键
func main(){
//make函数返回给定类型的映射,初始化备用
m = make(map[string]Vertex)
m["Bell"] = Vertex{
3.8, 32.44,
}
fmt.Println(m["Bell"])
}
//map文法,此m不是nil,可以直接添加键
var m = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}
//如果顶级类型只是一个类型名,可以在文法元素中忽略它
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
//插入或修改元素
m[key] = elem
//获取元素
elem = m[key]
//删除元素
delete(m,key)
//检查某个键是否存在
//如果key在m中,ok为true
//如果不在,ok为false,elem为映射元素的零值
elem, ok := m[key]
函数指针
func compute(fn func(float64, float64) float64) float64 {
return fn(3, 4)
}
func main() {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(compute(hypot))
fmt.Println(compute(math.Pow))
}
函数闭包
//Go 函数可以是一个闭包。闭包是一个函数值,它引用了其函数体之外的变量。该函数可以访问并赋予其引用的变量的值,换句话说,该函数被“绑定”在了这些变量上。
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
//其实这就相当于static变量,pos函数公用一个sum变量,neg函数共用一个变量
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
Go的方法
//Go中没有类,但是可以为结构体类型定义方法
//方法是带接收者参数的函数,位于func关键字和方法名之间
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(Vertex v) float64{
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
没有区别*/
//指针接收者,这个就基本相当于C++类中的成员函数了,会直接更改成员变量的值
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
v.Scale(10)
fmt.Println(v.Abs())
}
//注意v.Scale(10)这个函数,go编译时会将其变成(&v).Scale(10)
// p := &Vertex{3, 4}
// p.Scale(10)
//上面两行代码与v.Scale(10)在结果上没有什么区别
Go的接口
type Abser interface {
Abs() float64
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat 实现了 Abser
fmt.Println(a.Abs())
a = &v // a *Vertex 实现了 Abser
//由于a Vertex 没有实现 Abser,所以不能使 a = v
fmt.Println(a.Abs())
}
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
//接口值,接口也是值,可以像其他值一样传递
//接口值可以用作函数的参数或者返回值
//在内部,接口值可以看作包含值和具体类型的元组(value, type)
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
fmt.Println(t.S)
}
type F float64
func (f F) M() {
fmt.Println(f)
}
func main() {
var i I
i = &T{"Hello"}
describe(i)
i.M()
i = F(math.Pi)
describe(i)
i.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
/*OUTPUT
(&{Hello}, *main.T)
Hello
(3.141592653589793, main.F)
3.141592653589793
*/
//底层值为nil的接口值
//C语言,如果一个类指针为空,调用这个指针的方法会报错
//但是在Go语言中,仍然可以成功调用,不过需要特殊处理一下
//注意,保存了nil底层类型的接口,其本身并不是nil
func (t *T) M() {
if t == nil {
fmt.Println("This is <nil>")
return
}
fmt.Println("yesyes:=")
}
func main() {
var i I
var t *T
i = t
describe(i)
i.M()
}
/*OUTPUT
(<nil>, *main.T)
This is <nil>
*/
//如果接口值为nil,即没有指向任何变量。
//这时如果调用方法,会产生错误
//空接口可以保存任何类型的值
//空接口 interface{}
//空接口常用来处理未知类型的值
var i interface{}
i = 42
describe(i)
//类型断言
//类型断言提供了访问接口值底层具体值的方法
//t := i.(Type)
type T struct{
a,b int
}
func main() {
var i interface{} = T{3,5}
s, ok := i.(int)
fmt.Println(s, ok)
f, ok := i.(T)
fmt.Println(f, ok)
//f = i.(float64) // 报错(panic)
//fmt.Println(f)
}
//接口类型选择
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
/*fmt包中定义了Stringer接口
type Stringer interface {
String() string
}
Stringer是一个可以用字符串描述自己的类型
*/
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z)
}
/*OUTPUT
Arthur Dent (42 years) Zaphod Beeblebrox (9001 years)
*/
//重写error输出
//定义自己的error类型
type ErrNegativeSqrt float64
//编写fmt.Println(error)
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprint(float64(e))
}
//在需要的时候抛出自己的error类型
func Sqrt(x float64) (float64, error) {
if x >= 0{
return 0, nil
} else{
return 22, ErrNegativeSqrt(88.77)
}
}
//如果error为结构体类型,可以抛出error指针
type MyError struct {
When time.Time
What string
}
func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %s",
e.When, e.What)
}
func run() error {
return &MyError{
time.Now(),
"it didn't work",
}
}