开发环境
安装Golang
配置go的开发环境
vscode
Golang (JetBrain开发的IDE
基础语法
Hello World
1 2 3 4 5 6 7 8 package mainimport "fmt" func main () { fmt.Println("Hello, World!" ) }
第一行代码 package main
定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package
main。package main表示一个可独立执行的程序,每个 Go
应用程序都包含一个名为 main 的包。
下一行 import "fmt" 告诉 Go 编译器这个程序需要使用 fmt
包(的函数,或其他元素),fmt 包实现了格式化
IO(输入/输出)的函数。
下一行 func main() 是程序开始执行的函数。main
函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有
init() 函数则会先执行该函数)。
下一行 /... /
是注释,在程序执行时将被忽略。单行注释是最常见的注释形式,你可以在任何地方使用以
// 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */
结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
下一行
fmt.Println(...)
可以将字符串输出到控制台,并在最后自动增加换行字符
。 使用 fmt.Print("hello, world\n")
可以得到相同的结果。
Print 和 Println
这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量
arr 输出到控制台。
当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的
public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的
protected )。
需要注意的是 {
不能单独放在一行,所以以下代码在运行时会产生错误:
实例
1 2 3 4 5 6 package mainimport "fmt" func main () { * fmt.Println("Hello, World!" ) }
行分隔符
在 Go 程序中,一行代表一个语句结束 。每个语句不需要像
C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go
编译器自动完成。
如果你打算将多个语句写在同一行,它们则必须使用 ;
人为区分,但在实际开发中我们并不鼓励这种做法。
标识符
标识符用来命名变量、类型等程序实体。一个标识符实际上就是一个或是多个字母(A~Z和a~z)
、数字(0~9)
、下划线_
组成的序列,但是第一个字符 必须是字母或下划线而不能是数字 。
格式化字符串
Go 语言中使用 fmt.Sprintf 或
fmt.Printf 格式化字符串并赋值给新串:
Sprintf
根据格式化参数生成格式化的字符串并返回该字符串。
Printf
根据格式化参数生成格式化的字符串并写入标准输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport ( "fmt" )func main () { var stockcode=123 var enddate="2020-12-31" var url="Code=%d&endDate=%s" var target_url=fmt.Sprintf(url,stockcode,enddate) fmt.Println(target_url) }
输出结果为:
1 Code=123&endDate=2020-12-31
数据类型
布尔型bool
数字类型int
字符串类型
派生类型
指针
数组
结构化
Channel
函数
切片
接口interface
Map
数字类型
uint8 无符号 8 位整型 (0 到 255)
uint16 无符号 16 位整型 (0 到 65535)
uint32 无符号 32 位整型 (0 到 4294967295)
uint64 无符号 64 位整型 (0 到
18446744073709551615)
int8 有符号 8 位整型 (-128 到 127)
int16 有符号 16 位整型 (-32768 到 32767)
int32 有符号 32 位整型 (-2147483648 到
2147483647)
int64 有符号 64 位整型 (-9223372036854775808 到
9223372036854775807)
浮点型
float32 IEEE-754 32位浮点型数
float64 IEEE-754 64位浮点型数
complex64 32 位实数和虚数
complex128 64 位实数和虚数
其他数字类型
byte 类似 uint8
rune 类似 int32
uint 32 或 64 位
int 与 uint 一样大小
uintptr 无符号整型,用于存放一个指针
语言变量
1 2 var identifier1, identifier2 type var b, c int = 1 , 2
bool类型的默认初值为false
以下几种类型的初值为nil
1 2 3 4 5 6 var a *int var a []int var a map [string ] int var a chan int var a func (string ) int var a error
值类型和引用类型
变量间的赋值为值拷贝
一个引用类型的变量 r1 存储的是 r1
的值所在的内存地址(数字),或内存地址中第一个字所在的位置。当使用赋值语句
r2 = r1 时,只有引用(地址)被复制。如果 r1
的值被改变了,那么这个值的所有引用都会指向被修改后的内容,在这个例子中,r2
也会受到影响。
语言常量
常量是一个简单值的标识符,在程序运行时,不会被修改的量 。
常量中的数据类型只可以是布尔型bool
、数字型(整数型、浮点型和复数)
和字符串型
。
常量的定义格式:
1 const identifier [type ] = value
iota
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const
中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const
语句块中的行索引)。
iota 可以被用作枚举值:
1 2 3 4 5 const ( a = iota b = iota c = iota )
第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加
1;所以 a=0, b=1, c=2 可以简写为如下形式:
1 2 3 4 5 const ( a = iota b c )
iota 用法
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport "fmt" func main () { const ( a = iota b c d = "ha" e f = 100 g h = iota i ) fmt.Println(a,b,c,d,e,f,g,h,i) }
以上实例运行结果为:
再看个有趣的的 iota 实例:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport "fmt" const ( i=1 <<iota j=3 <<iota k l )func main () { fmt.Println("i=" ,i) fmt.Println("j=" ,j) fmt.Println("k=" ,k) fmt.Println("l=" ,l) }
以上实例运行结果为:
iota 表示从 0 开始自动加 1,所以 i=1<<0 ,
j=3<<1 (<<
表示左移的意思),即:i=1, j=6,这没问题,关键在 k 和 l,从输出结果看
k=3<<2 ,l=3<<3 。
简单表述:
i=1 :左移 0 位,不变仍为 1。
j=3 :左移 1 位,变为二进制 110 ,即
6。
k=3 :左移 2 位,变为二进制
1100 ,即 12。
l=3 :左移 3 位,变为二进制
11000 ,即 24。
注:<<n==*(2^n) 。
条件语句
if else
1 2 3 4 5 6 if 7 %2 == 0 { fmt.Println("7 is even" ) }else { fmt.Println("7 is odd" ) }
switch
switch后面的变量名不需要加括号
go语言中不需要加break也不会继续往下跑完所有的case
default 不论放在哪都是最后执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 a := 2 switch a{ case 1 : fmt.Println("one" ) case 2 : fmt.Println("two" ) default : fmt.Println("other" ) } t := time.Now()switch { case t.Hour() < 12 : fmt.Println("It's before noon" ) default : fmt.Println("It's after noon" ) }
type-switch
1 2 3 4 5 6 7 8 9 10 switch x.(type ) { case type : statement(s); case type : statement(s); default : statement(s); }
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport "fmt" func main () { var x interface {} switch i := x.(type ) { case nil : fmt.Printf(" x 的类型 :%T" ,i) case int : fmt.Printf("x 是 int 型" ) case float64 : fmt.Printf("x 是 float64 型" ) case func (int ) float64 : fmt.Printf("x 是 func(int) 型" ) case bool , string : fmt.Printf("x 是 bool 或 string 型" ) default : fmt.Printf("未知型" ) } }
以上代码执行结果为:
fallthrough
使用 fallthrough 会强制执行后面的 case 语句,fallthrough
不会判断下一条 case 的表达式结果是否为 true。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package mainimport "fmt" func main () { switch { case false : fmt.Println("1、case 条件语句为 false" ) fallthrough case true : fmt.Println("2、case 条件语句为 true" ) fallthrough case false : fmt.Println("3、case 条件语句为 false" ) fallthrough case true : fmt.Println("4、case 条件语句为 true" ) case false : fmt.Println("5、case 条件语句为 false" ) fallthrough default : fmt.Println("6、默认 case" ) } }
以上代码执行结果为:
1 2 3 2、case 条件语句为 true 3、case 条件语句为 false 4、case 条件语句为 true
从以上代码输出的结果可以看出:switch 从第一个判断表达式为 true 的
case 开始执行,如果 case 带有 fallthrough,程序会继续执行下一条
case,且它不会去判断下一个 case 的表达式是否为 true。
循环
go语言中只有for循环,没有while、do while循环,也可以break
continue跳出或者继续
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 i := 1 for { fmt.Println("loop" ) break }for j:=7 ;j<9 ;j++{ fmt.Println(j) }for n:=0 ;n<5 ;n++{ if n%2 == 0 { continue } fmt.Println(n) }for i<=3 { fmt.Println(i) i = i + 1 }
goto
语句
1 2 3 4 5 goto label; .. . label: statement;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport "fmt" func main () { var a int = 10 LOOP: for a < 20 { if a == 15 { a = a + 1 goto LOOP } fmt.Printf("a的值为 : %d\n" , a) a++ } }
以上实例执行结果为:
1 2 3 4 5 6 7 8 9 10 a的值为 : 10 a的值为 : 11 a的值为 : 12 a的值为 : 13 a的值为 : 14 a的值为 : 16 a的值为 : 17 a的值为 : 18 a的值为 : 19
函数
函数定义格式
1 2 3 func function_name ( [parameter list] ) [return_types] { }
func:函数由 func 开始声明
function_name:函数名称,参数列表和返回值类型构成了函数签名。
parameter
list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
return_types:返回类型,函数返回一列值。return_types
是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types
不是必须的。
函数体:函数定义的代码集合。
实例
1 2 3 4 5 6 7 8 9 10 11 12 func max (num1, num2 int ) int { var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }
返回多个值
1 2 3 4 5 6 7 8 9 10 11 12 ackage mainimport "fmt" func swap (x, y string ) (string , string ) { return y, x }func main () { a, b := swap("Google" , "Runoob" ) fmt.Println(a, b) }
以上实例执行结果为:
函数参数
函数如果使用参数,该变量可称为函数的形参。
形参就像定义在函数体内的局部变量。
调用函数,可以通过两种方式来传递参数:
值传递:在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递:在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
默认情况下,Go
语言使用的是值传递,即在调用过程中不会影响到实际参数。
函数用法
函数作为另外一个函数的实参
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport ( "fmt" "math" )func main () { getSquareRoot := func (x float64 ) float64 { return math.Sqrt(x) } fmt.Println(getSquareRoot(9 )) }
闭包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package mainimport "fmt" func getSequence () func () int { i:=0 return func () int { i+=1 return i } }func main () { nextNumber := getSequence() fmt.Println(nextNumber()) fmt.Println(nextNumber()) fmt.Println(nextNumber()) nextNumber1 := getSequence() fmt.Println(nextNumber1()) fmt.Println(nextNumber1()) }
以上代码执行结果为:
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport ( "fmt" )type Circle struct { radius float64 }func main () { var c1 Circle c1.radius = 10.00 fmt.Println("圆的面积 = " , c1.getArea()) }func (c Circle) getArea() float64 { return 3.14 * c.radius * c.radius }
以上代码执行结果为:
变量作用域
局部变量:函数内定义
全局变量:函数外定义
形式参数:函数定义中的变量
数组
数组长度固定,在实际运用中,我们更多使用切片
数组的声明
1 2 var arrayname [size]dataTypevar balance [10 ]float32
1 2 var numbers = [5 ]int {1 , 2 , 3 , 4 , 5 } numbers := [5 ]int {1 , 2 , 3 , 4 , 5 }
如果数组长度不确定,可以使用...
代替数组的长度,编译器会根据元素个数自行推断数组的长度
1 2 3 var balance = [...]float32 {1000.0 , 2.0 , 3.4 , 7.0 , 50.0 } 或 balance := [...]float32 {1000.0 , 2.0 , 3.4 , 7.0 , 50.0 }
如果设置了数组的长度,我们还可以通过指定下标来初始化元素:
1 2 balance := [5 ]float32 {1 :2.0 ,3 :7.0 }
初始化数组中 {} 中的元素个数不能大于
[] 中的数字。
如果忽略 [] 中的数字不设置数组大小,Go
语言会根据元素的个数来设置数组的大小:
以上实例读取了第五个元素。数组元素可以通过索引(位置)来读取(或者修改),索引从
0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。
多维数组
Go 语言支持多维数组,以下为常用的多维数组声明方式:
1 var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type
以下实例声明了三维的整型数组:
1 var threedim [5 ][10 ][4 ]int
二维数组
二维数组是最简单的多维数组,二维数组本质上是由一维数组组成的。二维数组定义方式如下:
1 var arrayName [ x ][ y ] variable_type
初始化二维数组
多维数组可通过大括号来初始值。以下实例为一个 3 行 4
列的二维数组:
1 2 3 4 5 a := [3 ][4 ]int { {0 , 1 , 2 , 3 } , {4 , 5 , 6 , 7 } , {8 , 9 , 10 , 11 }, }
注意:
以上代码中倒数第二行的}
必须要有逗号 ,因为最后一行的}
不能单独一行 ,也可以写成这样:
1 2 3 4 a := [3][4]int{ {0, 1, 2, 3} , /* 第一行索引为 0 */ {4, 5, 6, 7} , /* 第二行索引为 1 */ {8, 9, 10, 11}} /* 第三行索引为 2 */
指针
我们都知道,变量是一种使用方便的占位符,用于引用计算机内存地址。
Go 语言的取地址符是
&
,放到一个变量前使用就会返回相应变量的内存地址。
以下实例演示了变量在内存中地址:
实例
1 2 3 4 5 6 package mainimport "fmt" func main () { var a int = 10 fmt.Printf("变量的地址: %x\n" , &a ) }
执行以上代码输出结果为:
声明
1 2 3 var var_name *var -type var ip *int var fp *float32
指针使用流程:
定义指针变量。
为指针变量赋值。
访问指针变量中指向地址的值。
在指针类型前面加上 * 号(前缀)来获取指针所指向的内容。
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "fmt" func main () { var a int = 20 var ip *int ip = &a fmt.Printf("a 变量的地址是: %x\n" , &a ) fmt.Printf("ip 变量储存的指针地址: %x\n" , ip ) fmt.Printf("*ip 变量的值: %d\n" , *ip ) }
以上实例执行输出结果为:
1 2 3 a 变量的地址是: 20818a220 ip 变量储存的指针地址: 20818a220 *ip 变量的值: 20
Go 空指针
当一个指针被定义后没有分配到任何变量时,它的值为
nil
。
nil
指针也称为空指针。
nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
一个指针变量通常缩写为 ptr。
实例
1 2 3 4 5 6 package mainimport "fmt" func main () { var ptr *int fmt.Printf("ptr 的值为 : %x\n" , ptr ) }
以上实例输出结果为:
空指针判断:
1 2 if(ptr != nil) /* ptr 不是空指针 */ if(ptr == nil) /* ptr 是空指针 */
结构体
1 2 3 4 5 6 type struct_variable_type struct { member definition member definition ... member definition }
1 2 3 variable_name := structure_variable_type {value1, value2...valuen} 或 variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package mainimport "fmt" type Books struct { title string author string subject string book_id int }func main () { fmt.Println(Books{"Go 语言" , "www.runoob.com" , "Go 语言教程" , 6495407 }) fmt.Println(Books{title: "Go 语言" , author: "www.runoob.com" , subject: "Go 语言教程" , book_id: 6495407 }) fmt.Println(Books{title: "Go 语言" , author: "www.runoob.com" }) }
输出结果为:
1 2 3 {Go 语言 www.runoob.com Go 语言教程 6495407} {Go 语言 www.runoob.com Go 语言教程 6495407} {Go 语言 www.runoob.com 0}
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package mainimport "fmt" type Books struct { title string author string subject string book_id int }func main () { var Book1 Books var Book2 Books Book1.title = "Go 语言" Book1.author = "www.runoob.com" Book1.subject = "Go 语言教程" Book1.book_id = 6495407 Book2.title = "Python 教程" Book2.author = "www.runoob.com" Book2.subject = "Python 语言教程" Book2.book_id = 6495700 fmt.Printf( "Book 1 title : %s\n" , Book1.title) fmt.Printf( "Book 1 author : %s\n" , Book1.author) fmt.Printf( "Book 1 subject : %s\n" , Book1.subject) fmt.Printf( "Book 1 book_id : %d\n" , Book1.book_id) fmt.Printf( "Book 2 title : %s\n" , Book2.title) fmt.Printf( "Book 2 author : %s\n" , Book2.author) fmt.Printf( "Book 2 subject : %s\n" , Book2.subject) fmt.Printf( "Book 2 book_id : %d\n" , Book2.book_id) }
以上实例执行运行结果为:
1 2 3 4 5 6 7 8 Book 1 title : Go 语言 Book 1 author : www.runoob.com Book 1 subject : Go 语言教程 Book 1 book_id : 6495407 Book 2 title : Python 教程 Book 2 author : www.runoob.com Book 2 subject : Python 语言教程 Book 2 book_id : 6495700
切片
切片可以任意更改长度
使用make
操作来创建一个切片
不支持负数索引
slice原理:
你可以声明一个未指定大小的数组来定义切片:
切片不需要说明长度。
或使用 make() 函数来创建切片:
1 2 3 var slice1 []type = make ([]type , len ) 也可以简写为 slice1 := make ([]type , len )
也可以指定容量,其中 capacity 为可选参数。
1 make ([]T, length, capacity)
这里 len 是数组的长度并且也是切片的初始长度。
切片初始化
直接初始化切片,[]
表示是切片类型,{1,2,3} 初始化值依次是
1,2,3 ,其 cap=len=3 。
初始化切片 s ,是数组 arr 的引用。
1 s := arr[startIndex:endIndex]
将 arr 中从下标 startIndex 到 endIndex-1
下的元素创建为一个新的切片。
默认 endIndex 时将表示一直到arr的最后一个元素。
默认 startIndex 时将表示从 arr 的第一个元素开始。
1 s1 := s[startIndex:endIndex]
通过切片 s 初始化切片 s1。
通过内置函数 make()
初始化切片s ,[]int 标识为其元素类型为
int 的切片。
len()和cap()
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { var numbers = make ([]int ,3 ,5 ) printSlice(numbers) }func printSlice (x []int ) { fmt.Printf("len=%d cap=%d slice=%v\n" ,len (x),cap (x),x) }
以上实例运行输出结果为:
1 len=3 cap=5 slice=[0 0 0]
空切片
一个切片在未初始化之前默认为 nil,长度为 0
切片截取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package mainimport "fmt" func main () { numbers := []int {0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 } printSlice(numbers) fmt.Println("numbers ==" , numbers) fmt.Println("numbers[1:4] ==" , numbers[1 :4 ]) fmt.Println("numbers[:3] ==" , numbers[:3 ]) fmt.Println("numbers[4:] ==" , numbers[4 :]) numbers1 := make ([]int ,0 ,5 ) printSlice(numbers1) number2 := numbers[:2 ] printSlice(number2) number3 := numbers[2 :5 ] printSlice(number3) }func printSlice (x []int ) { fmt.Printf("len=%d cap=%d slice=%v\n" ,len (x),cap (x),x) }
执行以上代码输出结果为:
1 2 3 4 5 6 7 8 len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8] numbers == [0 1 2 3 4 5 6 7 8] numbers[1:4] == [1 2 3] numbers[:3] == [0 1 2] numbers[4:] == [4 5 6 7 8] len=0 cap=5 slice=[] len=2 cap=9 slice=[0 1] len=3 cap=7 slice=[2 3 4]
append() 和 copy()
1 2 3 4 5 6 var numbers []int numbers = append (numbers,1 ,2 ,3 ) numbers1 := make ([]int , len (numbers),(cap (numbers)*2 ))copy (numbers1,numbers)
Range
range 关键字用于 for
循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值 ,在集合中返回
key-value
对。
for 循环的 range 格式可以对
slice、map、数组、字符串等进行迭代循环。格式如下:
1 2 3 for key, value := range oldMap { newMap[key] = value }
以上代码中的 key 和 value 是可以省略。
如果只想读取 key,格式如下:
或者这样:
1 for key, _ := range oldMap
如果只想读取 value,格式如下:
1 for _, value := range oldMap
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package mainimport "fmt" func main () { map1 := make (map [int ]float32 ) map1[1 ] = 1.0 map1[2 ] = 2.0 map1[3 ] = 3.0 map1[4 ] = 4.0 for key, value := range map1 { fmt.Printf("key is: %d - value is: %f\n" , key, value) } for key := range map1 { fmt.Printf("key is: %d\n" , key) } for _, value := range map1 { fmt.Printf("value is: %f\n" , value) } }
以上实例运行输出结果为:
1 2 3 4 5 6 7 8 9 10 11 12 key is: 4 - value is: 4.000000 key is: 1 - value is: 1.000000 key is: 2 - value is: 2.000000 key is: 3 - value is: 3.000000 key is: 1 key is: 2 key is: 3 key is: 4 value is: 1.000000 value is: 2.000000 value is: 3.000000 value is: 4.000000
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package mainimport "fmt" func main () { nums := []int {2 , 3 , 4 } sum := 0 for _, num := range nums { sum += num } fmt.Println("sum:" , sum) for i, num := range nums { if num == 3 { fmt.Println("index:" , i) } } kvs := map [string ]string {"a" : "apple" , "b" : "banana" } for k, v := range kvs { fmt.Printf("%s -> %s\n" , k, v) } for i, c := range "go" { fmt.Println(i, c) } }
以上实例运行输出结果为:
1 2 3 4 5 6 sum: 9 index: 1 a -> apple b -> banana 0 103 1 111
Map
其他编程语言中可能叫做哈希or字典
完全无序
通过 key 来快速检索数据,key 类似于索引,指向数据的值。
在获取 Map 的值时,如果键不存在,返回该类型的零值,例如 int
类型的零值是 0,string 类型的零值是 ""。
Map 是引用类型,如果将一个 Map
传递给一个函数或赋值给另一个变量,它们都指向同一个底层数据结构,因此对
Map 的修改会影响到所有引用它的变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 map_variable := make (map [KeyType]ValueType, initialCapacity) m := make (map [string ]int ) m := make (map [string ]int , 10 ) m := map [string ]int { "apple" : 1 , "banana" : 2 , "orange" : 3 , }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 v1 := m["apple" ] v2, ok := m["pear" ] m["apple" ] = 5 len := len (m)for k, v := range m { fmt.Printf("key=%s, value=%d\n" , k, v) }delete (m, "banana" )
递归函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 func Factorial (n uint64 ) (result uint64 ) { if (n > 0 ) { result = n * Factorial(n-1 ) return result } return 1 } ---func fibonacci (n int ) int { if n < 2 { return n } return fibonacci(n-2 ) + fibonacci(n-1 ) } ---func sqrtRecursive (x, guess, prevGuess, epsilon float64 ) float64 { if diff := guess*guess - x; diff < epsilon && -diff < epsilon { return guess } newGuess := (guess + x/guess) / 2 if newGuess == prevGuess { return guess } return sqrtRecursive(x, newGuess, guess, epsilon) }func sqrt (x float64 ) float64 { return sqrtRecursive(x, 1.0 , 0.0 , 1e-9 ) }
类型转换
1 2 var a int = 10 var b float64 = float64 (a)
字符串str转换为整型变量num
1 2 3 4 5 6 7 var str string = "10" var num int num, _ = strconv.Atoi(str) num := 123 str := strconv.Itoa(num)
strconv.Atoi
函数返回两个值,第一个是转换后的整型值,第二个是可能发生的错误,我们可以使用空白标识符
**_** 来忽略这个错误
1 2 3 4 5 6 7 8 9 func main () { str := "123" num, err := strconv.Atoi(str) if err != nil { fmt.Println("转换错误:" , err) } else { fmt.Printf("字符串 '%s' 转换为整数为:%d\n" , str, num) } }
将字符串转换为浮点数
1 2 str := "3.14" num, err := strconv.ParseFloat(str, 64 )
将浮点数转换为字符串
1 2 num := 3.14 str := strconv.FormatFloat(num, 'f' , 2 , 64 )
接口类型转换
类型断言
将接口类型转换为指定类型
value 是接口类型的变量,type 或 T 是要转换成的类型。
1 2 3 value.(type ) 或者 value.(T)
1 2 3 4 5 6 7 var i interface {} = "Hello, World" str, ok := i.(string ) if ok { fmt.Printf("'%s' is a string\n" , str) } else { fmt.Println("conversion failed" ) }
以上实例中,我们定义了一个接口类型变量 i,并将它赋值为字符串 "Hello,
World"。然后,我们使用类型断言将 i
转换为字符串类型,并将转换后的值赋值给变量 str。最后,我们使用 ok
变量检查类型转换是否成功,如果成功,我们打印转换后的字符串;否则,我们打印转换失败的消息。
类型转换
将一个接口类型的值转换为另一个接口类型
T 是目标接口类型,value 是要转换的值。
在类型转换中,我们必须保证要转换的值和目标接口类型之间是兼容的,否则编译器会报错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport "fmt" type Writer interface { Write([]byte ) (int , error ) }type StringWriter struct { str string }func (sw *StringWriter) Write(data []byte ) (int , error ) { sw.str += string (data) return len (data), nil }func main () { var w Writer = &StringWriter{} sw := w.(*StringWriter) sw.str = "Hello, World" fmt.Println(sw.str) }
接口interface
错误处理
1 2 3 type error interface { Error() string }
我们可以在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。使用 errors.New
可返回一个错误信息:
1 2 3 4 5 6 func Sqrt (f float64 ) (float64 , error ) { if f < 0 { return 0 , errors.New("math: square root of negative number" ) } }
在下面的例子中,我们在调用 Sqrt 的时候传递的一个负数,然后就得到了
non-nil 的 error 对象,将此对象与 nil 比较,结果为 true,所以
fmt.Println
(fmt
包在处理 error 时会调用 Error
方法)被调用,以输出错误,请看下面调用的示例代码:
1 2 3 4 5 result, err:= Sqrt(-1 )if err != nil { fmt.Println(err) }