1.Go 语言教程
2.Go语言圣经(中文版)
3.Go语言高级编程
4.Go语言中文网
5.8小时转职Golang工程师(如果你想低成本学习Go语言)
package main //包声明
import "fmt" //引入包
func main() {
//函数
fmt.Println("Hello, World!") //语句 & 表达式
}
Go 语言的字符串可以通过+
实现:
package main //包声明
import "fmt" //引入包
func main() {
fmt.Println("Hello, " + "World")
}
package main
import (
"fmt"
)
func main() {
var stockCode = 123
var endDate = "2020-12-31"
// %d 表示整型数字,%s 表示字符串
var url = "Code=%d&endDate=%s"
var targetUrl = fmt.Sprintf(url, stockCode, endDate)
fmt.Println(targetUrl)
}
类型 | 描述 | 例子 |
---|---|---|
布尔型 | true 或者 false | var b bool = true |
数字类型 | 整型 和浮点型 | var stockCode int |
字符串类型 | 字符序列 | var endDate string |
派生类型:
(a) 指针类型(Pointer)
(b) 数组类型
(c) 结构化类型(struct)
(d) Channel 类型
(e) 函数类型
(f) 切片类型
(g) 接口类型(interface)
(h) Map 类型
fmt格式占位符
普通占位符
占位符 说明 举例 输出
%v 相应值的默认格式。 Printf("%v", people) {
zhangsan},
%+v 打印结构体时,会添加字段名 Printf("%+v", people) {
Name:zhangsan}
%#v 相应值的Go语法表示 Printf("#v", people) main.Human{
Name:"zhangsan"}
%T 相应值的类型的Go语法表示 Printf("%T", people) main.Human
%% 字面上的百分号,并非值的占位符 Printf("%%") %
布尔占位符
占位符 说明 举例 输出
%t true 或 false。 Printf("%t", true) true
整数占位符
占位符 说明 举例 输出
%b 二进制表示 Printf("%b", 5) 101
%c 相应Unicode码点所表示的字符 Printf("%c", 0x4E2D) 中
%d 十进制表示 Printf("%d", 0x12) 18
%o 八进制表示 Printf("%d", 10) 12
%q 单引号围绕的字符字面值,由Go语法安全地转义 Printf("%q", 0x4E2D) '中'
%x 十六进制表示,字母形式为小写 a-f Printf("%x", 13) d
%X 十六进制表示,字母形式为大写 A-F Printf("%x", 13) D
%U Unicode格式:U+1234,等同于 "U+%04X" Printf("%U", 0x4E2D) U+4E2D
Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。
声明变量的一般形式是使用 var 关键字:
var
仅指定变量类型,没有初始化,则变量默认为零值。
package main
import "fmt"
func main() {
// 声明一个变量并初始化
var a = "go"
fmt.Println(a)
// 没有初始化就为零值
var b int
fmt.Println(b)
// bool 零值为 false
var c bool
fmt.Println(c)
}
var d = true
fmt.Println(d)
:=
package main
import "fmt"
func main() {
str := "go"
fmt.Println(str)
}
如果变量已经使用 var 声明过了,再使用 := 声明变量,就产生编译错误,格式:
str := "go"
相等于:var str = "go"
package main
import "fmt"
var x, y int
var ( // 这种因式分解关键字的写法一般用于声明全局变量
a int
b bool
)
var c, d int = 1, 2
var e, f = 123, "hello"
func main(){
//这种不带声明格式的只能在函数体中出现
g, h := 123, "hello"
fmt.Println(x, y, a, b, c, d, e, f, g, h)
}
所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值:
当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝:
你可以通过&i
来获取变量 i 的内存地址,例如:0xf840000040(每次的地址都可能不一样)。值类型的变量的值存储在栈中。
package main
import "fmt"
func main(){
str := "go"
fmt.Println(str)
fmt.Println(&str)
}
const
用const
声明常量
const WIDTH int = 5
iota
iota
是一个特殊常量,可以认为是一个可以被编译器修改的常量。
package main
import "fmt"
const (
// 可以在const() 添加一个关键字 iota, 每行的iota都会累加1, 第一行的iota的默认值是0
ID1 = iota //iota = 0
ID2 //iota = 1
ID3 //iota = 2
)
func main() {
fmt.Printf("ID1:%d\n", ID1)
fmt.Printf("ID2:%d\n", ID2)
fmt.Printf("ID3:%d\n", ID3)
}
package main
import "fmt"
const (
// 可以在const() 添加一个关键字 iota, 每行的iota都会累加1, 第一行的iota的默认值是0
ID1 = 10*iota
ID2
ID3
)
func main() {
fmt.Printf("ID1:%d\n", ID1)
fmt.Printf("ID2:%d\n", ID2)
fmt.Printf("ID3:%d\n", ID3)
}
const (
a, b = iota+1, iota+2 // iota = 0, a = iota + 1, b = iota + 2, a = 1, b = 2
c, d // iota = 1, c = iota + 1, d = iota + 2, c = 2, d = 3
e, f // iota = 2, e = iota + 1, f = iota + 2, e = 3, f = 4
g, h = iota * 2, iota *3 // iota = 3, g = iota * 2, h = iota * 3, g = 6, h = 9
i, k // iota = 4, i = iota * 2, k = iota * 3 , i = 8, k = 12
)
iota 只能够配合const() 一起使用, iota只有在const进行累加效果。
Go 语言运算符
package main
import "fmt"
func main() {
a := 2
if a == 0{
}else if a == 1{
}else{
fmt.Println(a)
}
}
方式一:switch {}
package main
import "fmt"
func main() {
var grade string = "B"
switch {
case grade == "A":
fmt.Printf("优秀!\n")
case grade == "B", grade == "C":
fmt.Printf("良好\n")
case grade == "D":
fmt.Printf("及格\n")
case grade == "F":
fmt.Printf("不及格\n")
default:
fmt.Printf("差\n")
}
}
方式二:switch v_name{}
package main
import "fmt"
func main() {
var grade string = "B"
switch grade {
case "A":
fmt.Printf("优秀!\n")
case "B", "C":
fmt.Printf("良好\n")
case "D":
fmt.Printf("及格\n")
case "F":
fmt.Printf("不及格\n")
default:
fmt.Printf("差\n")
}
}
select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。
select 随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。一个默认的子句应该总是可运行的。
package main
import (
"fmt"
"strconv"
)
func main() {
for i :=0;i<10;i++ {
fmt.Printf(strconv.Itoa(i))
}
}
package main
import "fmt"
func main() {
/* 定义局部变量 */
var a int = 100
var b int = 200
var ret int
/* 调用函数并返回最大值 */
ret = max(a, b)
fmt.Printf( "最大值是 : %d\n", ret )
}
/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
/* 定义局部变量 */
var result int
if num1 > num2 {
result = num1
} else {
result = num2
}
return result
}
package main
import "fmt"
// (x, y string) : 要传入的参数及类型
// (string, string) : 要返回的类型
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("Google", "Runoob")
fmt.Println(a, b)
}
//返回多个返回值,匿名的
func foo2(a string, b int) (int, int) {
//...
return 666, 777
}
return
后就不用再写了//返回多个返回值, 有形参名称的
func foo3(a string, b int) (r1 int, r2 int) {
//...
//给有名称的返回值变量赋值
r1 = 1000
r2 = 2000
return
}
返回值的类型相返回值还可以这样写
//返回多个返回值, 有形参名称的
func foo3(a string, b int) (r1 , r2 int) {
//...
//给有名称的返回值变量赋值
r1 = 1000
r2 = 2000
return
}
当然 传入参数值的类型相返回值也可以这样写
//返回多个返回值, 有形参名称的
func foo3(a , b int) (r1 , r2 int) {
//...
//给有名称的返回值变量赋值
r1 = 1000
r2 = 2000
return
}
函数如果使用参数,该变量可称为函数的形参。形参就像定义在函数体内的局部变量。调用函数,可以通过两种方式来传递参数:
传递类型 | 描述 |
---|---|
值传递 | 值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。 |
引用传递(传地址) | 引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。 |
引用传递:
package main
import "fmt"
func main() {
/* 定义局部变量 */
var a int = 100
var b int= 200
fmt.Printf("交换前,a 的值 : %d\n", a )
fmt.Printf("交换前,b 的值 : %d\n", b )
/* 调用 swap() 函数 交换地址
* &a 指向 a 指针,a 变量的地址
* &b 指向 b 指针,b 变量的地址
*/
swap(&a, &b)
fmt.Printf("交换后,a 的值 : %d\n", a )
fmt.Printf("交换后,b 的值 : %d\n", b )
}
func swap(x *int, y *int) {
var temp int
temp = *x /* 保存 x 地址上的值 */
*x = *y /* 将 y 值赋给 x */
*y = temp /* 将 temp 值赋给 y */
}
函数用法 | 描述 |
---|---|
函数作为另外一个函数的实参 | 函数定义后可作为另外一个函数的实参数传入 |
闭包 | 闭包是匿名函数,可在动态编程中使用 |
方法 | 方法就是一个包含了接受者的函数 |
package main
import (
"fmt"
"math"
)
func main(){
/* 声明函数变量 */
getSquareRoot := func(x float64) float64 {
return math.Sqrt(x)
}
/* 使用函数 */
fmt.Println(getSquareRoot(9))
}
Go 语言支持匿名函数,可作为闭包。匿名函数是一个"内联"语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。
package main
import "fmt"
func getSequence() func() int {
i:=0
return func() int {
i+=1
return i
}
}
func main(){
/* nextNumber 为一个函数,函数 i 为 0 */
nextNumber1 := getSequence()
/* 调用 nextNumber 函数,i 变量自增 1 并返回 */
fmt.Println(nextNumber1()) // 输出 1
fmt.Println(nextNumber1()) // 输出 2
fmt.Println(nextNumber1()) // 输出 3
/* 创建新的函数 nextNumber1,并查看结果 */
nextNumber2 := getSequence()
fmt.Println(nextNumber2()) // 输出 1
fmt.Println(nextNumber2()) // 输出 2
}
Go 语言中同时有函数和方法。一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。
package main
import (
"fmt"
)
// Circle 定义结构体
type Circle struct {
radius float64
}
func main() {
var c1 Circle
c1.radius = 10.00
fmt.Println("圆的面积 = ", c1.getArea())
}
//该 method 属于 Circle 类型对象中的方法
func (c Circle) getArea() float64 {
//c.radius 即为 Circle 类型对象中的属性
return 3.14 * c.radius * c.radius
}
var n [10]int /* n 是一个长度为 10 的数组 */
var balance = [5]float32{
1000.0, 2.0, 3.4, 7.0, 50.0}
var balance = [...]float32{
1000.0, 2.0, 3.4, 7.0, 50.0}
// 将索引为 1 和 3 的元素初始化
balance := [5]float32{
1:2.0,3:7.0}
package main
import "fmt"
func main() {
var n [10]int /* n 是一个长度为 10 的数组 */
var i,j int
/* 为数组 n 初始化元素 */
for i = 0; i < 10; i++ {
n[i] = i + 100 /* 设置元素为 i + 100 */
}
/* 输出每个数组元素的值 */
for j = 0; j < 10; j++ {
fmt.Printf("Element[%d] = %d\n", j, n[j] )
}
}
for i = 0; i < 10; i++ {
fmt.Println("index = ", i, ", value = ", myArray[i])
}
for index, value := range myArray {
fmt.Println("index = ", index, ", value = ", value)
}
_
表示匿名变量
for _, value := range myArray {
fmt.Println("index = ", index, ", value = ", value)
}
package main
import "fmt"
func printData(arr [12]int){
fmt.Println(arr)
}
func main() {
myArray := [12]int{
1,2,4,5}
printData(myArray)
}
动态数组其实就是切片,请看12、Go 语言切片(Slice)
package main
import "fmt"
func printData(arr []int){
fmt.Println(arr)
}
func main() {
myArray := []int{
1,2,4,5}
printData(myArray)
}
一个指针变量指向了一个值的内存地址。
package main
import "fmt"
func main() {
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* ip的值赋为a的存储地址 */
fmt.Printf("a 变量的值: %d\n", a)
fmt.Printf("a 变量的地址是: %x\n", &a )
/* 指针变量的存储地址 */
fmt.Printf("ip 变量的值: %x\n", ip )
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip )
}
空指针:当一个指针被定义后没有分配到任何变量时,它的值为 nil。
nil 指针也称为空指针。
nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
一个指针变量通常缩写为 ptr。
空指针判断:
if(ptr != nil) /* ptr 不是空指针 */
if(ptr == nil) /* ptr 是空指针 */
如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。
当定义一个指向指针的指针变量时,第一个指针存放第二个指针的地址,第二个指针存放变量的地址:
指向指针的指针变量声明格式如下:
var ptr **int;
package main
import "fmt"
func main() {
var a int
var ptr *int
var pptr **int
a = 3000
ptr = &a
pptr = &ptr
fmt.Printf("a = %d\n", a)
fmt.Printf("ptr = %d\n", ptr)
fmt.Printf("*ptr = %d\n", *ptr)
fmt.Printf("pptr = %d\n", pptr)
fmt.Printf("*pptr = %d\n", *pptr)
fmt.Printf("**pptr = %d\n", **pptr)
}
package main
import "fmt"
func main() {
/* 定义局部变量 */
var a int = 100
var b int= 200
fmt.Printf("交换前 a 的值 : %d\n", a )
fmt.Printf("交换前 b 的值 : %d\n", b )
/* 调用函数用于交换值
* &a 指向 a 变量的地址
* &b 指向 b 变量的地址
*/
swap(&a, &b);
fmt.Printf("交换后 a 的值 : %d\n", a )
fmt.Printf("交换后 b 的值 : %d\n", b )
}
func swap(x *int, y *int) {
var temp int
temp = *x /* 保存 x 地址的值 */
*x = *y /* 将 y 赋值给 x */
*y = temp /* 将 temp 赋值给 y */
}
声明一种行的数据类型 myint, 是int的一个别名
type myint int
package main
import "fmt"
// Books 定义一个结构体
type Books struct {
title string
author string
subject string
bookId int
}
func main() {
// 创建一个新的结构体
book1 := Books{
"Go 语言", "www.runoob.com", "Go 语言教程", 6495407}
fmt.Println(book1)
// 也可以使用 key => value 格式
book2 := Books{
title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", bookId: 6495407}
fmt.Println(book2)
// 忽略的字段为 0 或 空
book3 := Books{
title: "Go 语言", author: "www.runoob.com"}
fmt.Println(book3)
}
package main
import "fmt"
type Books struct {
title string
author string
subject string
bookId int
}
func main() {
var Book1 Books /* 声明 Book1 为 Books 类型 */
var Book2 Books /* 声明 Book2 为 Books 类型 */
/* book1.描述 */
Book1.title = "Go 语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.bookId = 6495407
/* book2.描述 */
Book2.title = "Python 教程"
Book2.author = "www.runoob.com"
Book2.subject = "Python 语言教程"
Book2.bookId = 6495700
/* 打印 Book1 信息 */
fmt.Printf( "Book1.title : %s\n", Book1.title)
fmt.Printf( "Book1.author : %s\n", Book1.author)
fmt.Printf( "Book1.subject : %s\n", Book1.subject)
fmt.Printf( "Book1.bookId : %d\n", Book1.bookId)
/* 打印 Book2 信息 */
fmt.Printf( "Book2.title : %s\n", Book2.title)
fmt.Printf( "Book2.author : %s\n", Book2.author)
fmt.Printf( "Book2.subject : %s\n", Book2.subject)
fmt.Printf( "Book2.bookId : %d\n", Book2.bookId)
}
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books /* 声明 Book1 为 Books 类型 */
/* book 1 描述 */
Book1.title = "Go 语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407
/* 打印 Book1 信息 */
printBook(Book1)
}
func printBook( book Books ) {
fmt.Printf( "Book title : %s\n", book.title)
fmt.Printf( "Book author : %s\n", book.author)
fmt.Printf( "Book subject : %s\n", book.subject)
fmt.Printf( "Book book_id : %d\n", book.book_id)
}
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books /* 声明 Book1 为 Books 类型 */
/* book 1 描述 */
Book1.title = "Go 语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407
/* 打印 Book1 信息 */
printBook(&Book1)
}
func printBook( book *Books ) {
fmt.Printf( "Book title : %s\n", book.title)
fmt.Printf( "Book author : %s\n", book.author)
fmt.Printf( "Book subject : %s\n", book.subject)
fmt.Printf( "Book book_id : %d\n", book.book_id)
}
Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
声明一个未指定大小的数组来定义切片:
数组初始化:
array := [3]int {
1,2,3 }
切片初始化:
slice := []int {
1,2,3 }
直接初始化切片,[]
表示是切片类型,{1,2,3} 初始化值依次是 1,2,3,其 cap=len=3
。
slice := []int {
1,2,3 }
2&3.切片不需要说明长度。或使用make()
函数(开辟空间)来创建切片:
var slice1 []type = make([]type, len)
也可以简写为
slice1 := make([]type, len)
3.也可以指定容量,其中 capacity
为可选参数。
make([]T, length, capacity)
slice:= array[startIndex:endIndex]
package main
import "fmt"
func main() {
array := [5]int {
1,2,3,4,5}
slice := array[1:4]
fmt.Println(slice)
}
切片是可索引的,并且可以由 len()
方法获取长度。
切片提供了计算容量的方法cap()
可以测量切片最长可以达到多少。
package main
import "fmt"
func main() {
arrayInt := []int{
1,2,3,4}
fmt.Printf("len=%d cap=%d slice=%v\n",len(arrayInt),cap(arrayInt),arrayInt)
}
package main
import "fmt"
func main() {
var numbers []int
if numbers == nil {
fmt.Printf("切片是空的")
}
}
package main
import "fmt"
func main() {
arrayInt := [] int {
0,1,2,3,4,5,6}
fmt.Println(arrayInt[1:4])
//[:4] => [0:4]
fmt.Println(arrayInt[:4])
// [1:] => [1:len(arrayInt)]
fmt.Println(arrayInt[1:])
}
package main
import "fmt"
func main() {
var numbers []int
printSlice(numbers)
/* 允许追加空切片 */
numbers = append(numbers, 0)
printSlice(numbers)
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
printSlice(numbers)
/* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
printSlice(numbers)
/* 创建切片 numbers1 是之前切片的两倍容量*/
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers)
printSlice(numbers1)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。
package main
import "fmt"
func main() {
//这是我们使用range去求一个slice的和。使用数组跟这个很类似
nums := []int{
2, 3, 4}
sum := 0
//我们不需要使用该元素的序号,所以我们使用空白符"_"省略了
for _, num := range nums {
sum += num
}
fmt.Println("sum:", sum)
//在数组上使用range将传入index和值两个变量。
for i, num := range nums {
if num == 3 {
fmt.Println("index:", i)
}
}
//range也可以用在map的键值对上。
kvs := map[string]string{
"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
//range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
for i, c := range "go" {
fmt.Println(i, c)
}
}
Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。
可以使用内建函数 make 也可以使用 map 关键字来定义 Map:
一般都要初始化值或用make开辟空间,开辟空间少了空间也会自动增
如果不初始化 map,那么就会创建一个 nil map。nil map 不能用来存放键值对,一般都要初始化值或用make开辟空间
// 声明变量,默认 map 是 nil
var myMapName map[键的数据类型]值的数据类型
var myMap map[string]string
myMap3 := map[string]string{
"one": "php",
"two": "c++",
"three": "python",
}
// 使用 make 函数,
myMapName := make(map[键的数据类型]值的数据类型)
myMap := make(map[string]string)
myMap := make(map[string]string,len)
myMap := make(map[string]string,10)
package main
import "fmt"
func main() {
capitalMap := make(map[string]string)
/* map插入key - value对,各个国家对应的首都 */
capitalMap [ "France" ] = "巴黎"
capitalMap [ "Italy" ] = "罗马"
capitalMap [ "Japan" ] = "东京"
capitalMap [ "India " ] = "新德里"
/*使用键输出地图值 */
for key,value := range capitalMap {
fmt.Println(key, "首都是", value)
}
}
//增
capitalMap[ "France" ] = "巴黎"
//删
delete(countryCapitalMap, "France")
//改
capitalMap[ "France" ] = "巴黎2"
//查
fmt.Println(capitalMap[ "France" ])
delete() 函数:
package main
import "fmt"
func main() {
/* 创建map */
countryCapitalMap := map[string]string{
"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}
fmt.Println("原始地图")
/* 打印地图 */
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap [ country ])
}
/*删除元素*/
delete(countryCapitalMap, "France")
fmt.Println("法国条目被删除")
fmt.Println("删除元素后地图")
/*打印地图*/
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap [ country ])
}
}
package main
import "fmt"
func ChangeValue(cityMap map[string]string) {
cityMap["France"] = "巴黎2"
}
func main() {
capitalMap := make(map[string]string)
/* map插入key - value对,各个国家对应的首都 */
capitalMap [ "France" ] = "巴黎"
capitalMap [ "Italy" ] = "罗马"
capitalMap [ "Japan" ] = "东京"
capitalMap [ "India " ] = "新德里"
ChangeValue(capitalMap)
fmt.Println(capitalMap)
}
递归,就是在运行的过程中调用自己。
package main
import "fmt"
func Factorial(n uint64)(result uint64) {
if n > 0 {
result = n * Factorial(n-1)
return result
}
return 1
}
func main() {
var i int = 3
fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(uint64(i)))
}
package main
import "fmt"
func fibonacci(n int) int {
if n < 2 {
return n
}
return fibonacci(n-2) + fibonacci(n-1)
}
func main() {
var i int
for i = 0; i < 10; i++ {
fmt.Printf("%d\t", fibonacci(i))
}
}
类型转换用于将一种数据类型的变量转换为另外一种类型的变量。
go 不支持隐式转换类型
package main
import "fmt"
func main() {
var sum int = 17
var count int = 5
var mean float32
mean = float32(sum)/float32(count)
fmt.Printf("mean 的值为: %f\n",mean)
}
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
定义接口
package main
import (
"fmt"
)
// NokiaPhone 定义一个结构体
type NokiaPhone struct {
}
// IPhone 定义一个结构体
type IPhone struct {
}
// Phone 定义了一个接口Phone
type Phone interface {
call()
}
func (nokiaPhone NokiaPhone) call() {
fmt.Println("This is NokiaPhone!")
}
func (iPhone IPhone) call() {
fmt.Println("This is IPhone!")
}
func main() {
var phone Phone
//根据传入参数的类型调用不同的方法
phone = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}
Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
error类型是一个接口类型,这是它的定义:
type error interface {
Error() string
}
我们可以在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息:
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("math: square root of negative number")
}
// 实现
}
实例:
package main
import (
"fmt"
)
// DivideError 定义一个 DivideError 结构
type DivideError struct {
dividee int
divider int
}
// 实现 `error` 接口
func (de *DivideError) Error() string {
strFormat := `
Cannot proceed, the divider is zero.
dividee: %d
divider: 0
`
return fmt.Sprintf(strFormat, de.dividee)
}
// Divide 定义 `int` 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
if varDivider == 0 {
dData := DivideError{
dividee: varDividee,
divider: varDivider,
}
errorMsg = dData.Error()
return
} else {
return varDividee / varDivider, ""
}
}
func main() {
// 正常情况
if result, errorMsg := Divide(100, 10); errorMsg == "" {
fmt.Println("100/10 = ", result)
}
// 当除数为零的时候会返回错误信息
if _, errorMsg := Divide(100, 0); errorMsg != "" {
fmt.Println("errorMsg is: ", errorMsg)
}
}
并发:并行出发、并行执行
Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。
goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。
goroutine 语法格式:
go 函数名( 参数列表 )
Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间。
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
执行以上代码,你会看到输出的 hello 和 world 是没有固定先后顺序。因为它们是两个 goroutine 在执行:
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
//并发
go say("new 1")
say("new 2")
}
通道(channel)是用来传递数据的一个数据结构。
通道可用于两个 goroutine(线程) 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
ch <- v // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据,并把值赋给 v
定义一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:
ch := make(chan int)
注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。
以下实例通过两个 goroutine(线程) 来计算数字之和,在 goroutine(线程) 完成计算后,它会计算两个结果的和:
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // 把 sum 发送到通道 c
}
func main() {
s := []int{
7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // 从通道 c 中接收
fmt.Println(x, y, x+y)
}