go流程控制和集合 2

一、内置函数

名称介绍
append用来追加元素到数组、slice中,返回修改后的数组、slice
close主要用来关闭channel
delete从map中删除key对应的value
panic停止常规的goroutine (panic和recover:用来做错误处理)
recover允许程序定义goroutine的panic动作
real返回complex的实部 (complex、real imag:用于创建和操作复数)
imag返回complex的虚部
make用来分配内存,返回Type本身(只能应用于slice, map, channel)
new用来分配内存,主要用来分配值类型,比如int、struct。返回指向Type的指针
capcapacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
copy用于复制和连接slice,返回复制的数目
len来求长度,比如string、array、slice、map、channel ,返回长度
print、println底层打印函数,在部署环境中建议使用 fmt 包

二、流程控制

if

实际上和其他语言没有特别的区别只是语法上需要注意一下语法即可
• 可省略条件表达式括号。
• 持初始化语句,可定义代码块局部变量。
• 代码块左 括号必须在条件表达式尾部。

if 布尔表达式 {
	/* 在布尔表达式为 true 时执行 */
}

示例

if n := "abc"; x > 0 { // 初始化语句未必就是定义变量, 如 println("init") 也是可以的。
	println(n[2])
} else if x < 0 { // 注意 else if 和 else 左大括号位置。
	println(n[1])
} else {
	println(n[0])
}

不支持三元操作符(三目运算符) “a > b ? a : b”。

switch

switch 语句用于基于不同条件执行不同动作,每一个case 分支都是唯一的,从上直下逐一测试,直到匹配为止。Golang switch 分支表达式可以是任意类型,不限于常量。可省略 break,默认自动终止。

switch var1 {
	case val1:
		...
	case val2:
		...
	default:
		...
}

变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式。 您可以同时测试多个可能符合条件的值,使用逗号分割它们,例如:case val1, val2, val3。

/* 定义局部变量 */
var grade string = "B"
var marks int = 90
switch marks {
	case 90: grade = "A"
	case 80: grade = "B"
	case 50,60,70 : grade = "C"
	default: grade = "D"
}
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" )
}
fmt.Printf("你的等级是 %s\n", grade )

Type Switch
关于switch也可以用于type检查,switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型

switch x.(type){
	case type:
		statement(s)
	case type:
		statement(s)
	/* 你可以定义任意个数的case */
	default: /* 可选 */
		statement(s)
}

栗子:

package main
import "fmt"
type go_class struct {
	class_name string
	class_stage int
}
func main() {
	var g go_class
	g.class_name = "shineyork go class"
	g.class_stage = 1
	sw(10)
	sw("s")
	sw(g)
}
func sw(x interface{}) {
	switch x.(type) { // 带初始化语句
		case nil:
			fmt.Println(" x 的类型 nil\r\n")
		case int:
			fmt.Println("x 是 int 型")
		case float64:
			fmt.Println("x 是 float64 型")
		case func(int) float64:
			fmt.Println("x 是 func(int) 型")
		case bool, string:
			fmt.Println("x 是 bool 或 string 型")
		case go_class:
			fmt.Println("x 是 go_class 型")
		default:
			fmt.Println("未知型")
	}
}

fallthrough

var k = 0
switch k {
	case 0:
		println("fallthrough")
		fallthrough
	/*
	Go的switch非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;
	而如果switch没有表达式,它会匹配true。
	Go里面switch默认相当于每个case最后带有break,
	匹配成功后不会自动向下执行其他case,而是跳出整个switch, 但是可以使用fallthrough强制执行后面的case代码。
	*/
	case 1:
		fmt.Println("1")
	default:
		fmt.Println("def")
}

效果:

switch不写条件的时候当if与else运用

var n = 0
switch { //省略条件表达式,可当 if...else if...else
	case n > 0 && n < 10:
		fmt.Println("i > 0 and i < 10")
	case n > 10 && n < 20:
		fmt.Println("i > 10 and i < 20")
	default:
		fmt.Println("def")
}

for

Go语言的For循环有3中形式,只有其中的一种使用分号。

for init; condition; post { }
for condition { }
for { }
init: 一般为赋值表达式,给控制变量赋初值;
condition: 关系表达式或逻辑表达式,循环控制条件;
post: 一般为赋值表达式,给控制变量增量或减量。
for语句执行过程如下:
①先对表达式 init 赋初值;
②判别赋值表达式 init 是否满足给定 condition 条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行 post,进入第二次循环,再判别 condition;否则判断 condition 的值为假,不满足条件,就终止for循环,执行循环体外语句。

栗子:

func main() {
	s := "abc123"
	for i, n := 0, length(s); i < n; i++ { // 常见的 for 循环,支持初始化语句。
		println(s[i])
	}
	/*
call length.
97
98
99
49
50
51
*/
	n := len(s)
	for n > 0 { // 替代 while (n > 0) {}
		n--
		println(s[n]) // 替代 for (; n > 0;) {}
	}
	for { // 替代 while (true) {}
		println(s) // 替代 for (;;) {}
	}
}
func length(s string) int {
	println("call length.")
	return len(s)
}

三、指针

关于go中的指针实际上和PHP的 " & " 功能很是相似,不过还是有一点区别go语言中的指针只需要记住两个符号:&(取地址)和 *(根据地址取值)要搞明白Go语言中的指针需要先知道3个概念:指针地址、指针类型和指针取值。

在整之前回顾PHP中的&

在PHP中的&特点就是会引用

<?php
$a = 0;
$b = &$a;
$b++;
echo $a;
?>

输出结果为 1 ;


go中

package main

import (
	"flag"
	"fmt"
)

var mode = flag.String("mode","","process mode") //go run zhizhen.go --mode=fast
func swap(x,y *int){ //交换指针
	t:=*x
	*x=*y
	*y=t
}

func main() {
    // 解析命令行参数
    flag.Parse()
    // 输出命令行参数
    fmt.Println(*mode)
	x,y := 1,2
	swap(&x,&y)
	fmt.Println(x,y)

	n := 20
	ptr := &n
	fmt.Printf("数据类型ptr type:%T\n",ptr) //*int
	fmt.Printf("指针地址ptr address:%p\n",ptr) //0xc0000120e8
	fmt.Printf("指针的值ptr value:%v\n",ptr) //0xc0000120e8
	val := *ptr
	fmt.Printf("指针的*ptr值 value:%v\n",val) //20
	*ptr = 10
	fmt.Printf("指针的*ptr=10值 value:%v\n",*ptr) //10
	fmt.Printf("指针的n值 value:%v\n",n) //10
}
2 1
数据类型ptr type:*int
指针地址ptr address:0xc0000120b8
指针的值ptr value:0xc0000120b8
指针的*ptr值 value:20
指针的*ptr=10值 value:10
指针的n值 value:10

指针地址和指针类型

每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行“取地址”操作。 Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:*int、*int64、*string等。

取变量指针的语法如下:

ptr := &v // v的类型为T

其中:

v:代表被取地址的变量,类型为T
ptr:用于接收地址的变量,ptr的类型就为*T,称做T的指针类型。*代表指针。


栗子

var a int = 10
b := &a
fmt.Println(b) // 0xc0000a2090
fmt.Println(a) // 10
fmt.Println(&b) // 0xc0000cc020   
//地址 0xc0000a2090 0xc0000cc020 是会一直变的

总结: 取地址操作符&和取值操作符*是一对互补操作符,&取出地址,*根据地址取出地址指向的值。
变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:

  1. 对变量进行取地址(&)操作,可以获得这个变量的指针变量。
  2. 指针变量的值是指针地址。
  3. 对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。
func main() {
	a := 10
	test1(a)
	fmt.Println(a) //10
	test2(&a)  //传递指针地址
	fmt.Println(a) //0
}
func test1(x int) {
	x = 0
}
func test2(a *int) { //接收指针地址
	*a = 0  //指针地址重新赋值
}

10
0

空指针

当一个指针被定义后没有分配到任何变量时,它的值为 nil

var a *int
fmt.Println(a)
<nil>

四、数组

  1. 数组:是同一种数据类型的固定长度的序列。
  2. 数组定义:var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。
  3. 长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型。
  4. 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1
for i := 0; i < len(a); i++ { }
for index, v := range a { }
  1. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
  2. 数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
  3. 支持 “==”、"!=" 操作符,因为内存总是被初始化过的。
  4. 指针数组 [n]*T,数组指针 *[n]T。

一维数组
全局的定义方式也适合局部

全局:
var arr0 [5]int = [5]int{1, 2, 3}  //len = 5,cap = 5
var arr1 = [5]int{1, 2, 3, 4, 5}  //[5] ,{}里必须有五个
var arr2 = [...]int{1, 2, 3, 4, 5, 6}  //通过值去确定数组的长度
var str = [5]string{3: "hello world", 4: "tom"} //[4] 会报错
局部:
a := [3]int{1, 2} // 未初始化元素值为 0。
b := [...]int{1, 2, 3, 4} // 通过初始化值确定数组长度。
c := [5]int{2: 100, 4: 200} // 使用索引号初始化元素。[4] 就会报错,
d := [...]struct {
	name string
	age uint8
}{
	{"user1", 10}, // 可省略元素类型。
	{"user2", 20}, // 别忘了最后一行的逗号。
}

多维数组

var arr0 [5][3]int
var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}} 
a := [2][3]int{{1, 2, 3}, {4, 5, 6}}
b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "..."。

遍历 range

var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
for k1, v1 := range f {
	for k2, v2 := range v1 {
		fmt.Printf("(%d,%d)=%d ", k1, k2, v2)
	}
	fmt.Println() //换行的意思
}
(0,0)=1 (0,1)=2 (0,2)=3
(1,0)=7 (1,1)=8 (1,2)=9

数组定义

形参方式

void myFunction(param [10]int)
{}
void myFunction(param []int)
{}

与数组相关的函数
len – 来求长度,比如string、array、slice、map、channel ,返回长度

栗子:

var usernames [5]string = [5]string{"shineyork", "cara"}
fmt.Println(len(usernames))   //5

五、切片Slice

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大需要说明,slice 并不是数组或数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。

  1. 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。
  2. 切片的长度可以改变,因此,切片是一个可变的数组。
  3. 切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。
  4. cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。
  5. 切片的定义:var 变量名 []类型,比如 var str []string var arr []int。
  6. 如果 slice == nil,那么 len、cap 结果都等于 0。

切片Slice定义

声明一个未指定大小的数组来定义切片

var identifier \[]type

切片不需要说明长度 或者使用make()函数来创建切片

var slice1 []type = make([]type, len)
或者
slice1 := make([]type, len)
也可以指定容量,其中capacity为可选参数
make([]T,length,capacity)
这里的len是数组的长度并且也是切片的初始长度

我们来直接看栗子
栗子

//1.声明切片
var s1 []int
// 2.:=
s2 := []int{}
// 3.make()
var s3 []int = make([]int, 0)
var s4 []int = make([]int, 0, 0)
fmt.Println(s1, s2, s3, s4)

切片slice初始化

// 直接初始化切片,[]表示是切片类型,{1,2,3}初始化值依次是1,2,3.其cap=len=3
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}

// 初始化切片slice8,是数组arr的引用
var slice3 []int = arr[:]
slice8 := arr[:]

// 将arr中从下标start到end的元素创建为一个新的切片
var slice0 []int = arr[start:end]
slice5 := arr[start:end]

// 默认 end 时将表示一直到arr的最后一个元素
var slice2 []int = arr[start:]
slice7 := arr[start:]

// 默认 start 时将表示一直到arr的最后一个元素
var slice1 []int = arr[:end]
slice6 := arr[:end]

// 通过内置函数make()初始化切片s,[]int 标识为其元素类型为int的切片
s :=make([]int,len,cap)

栗子

arr := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[2:8]  
slice6 := arr[0:6] //可以简写为 slice := arr[:end]
slice7 := arr[5:10] //可以简写为 slice := arr[start:]
slice8 := arr[0:len(arr)] //slice := arr[:]
slice9 := arr[:len(arr)-1] //去掉切片的最后一个元素
fmt.Printf("局部变量: arr %v\n", arr)
fmt.Printf("局部变量: slice5 %v\n", slice5)  //[7-2]
fmt.Printf("局部变量: slice6 %v\n", slice6)  //[9-4]
fmt.Printf("局部变量: slice7 %v\n", slice7)  //[4-0]
fmt.Printf("局部变量: slice8 %v\n", slice8)  //[9-0]
fmt.Printf("局部变量: slice9 %v\n", slice9)  //[9-1]
局部变量: arr [9 8 7 6 5 4 3 2 1 0]
局部变量: slice5 [7 6 5 4 3 2]
局部变量: slice6 [9 8 7 6 5 4]
局部变量: slice7 [4 3 2 1 0]
局部变量: slice8 [9 8 7 6 5 4 3 2 1 0]
局部变量: slice9 [9 8 7 6 5 4 3 2 1]

值得注意的是读写操作实际目标是底层数组,只需注意索引号的差别。

data := [...]int{0, 1, 2, 3, 4, 5}
s := data[2:4]
s[0] += 100
s[1] += 200
fmt.Println(s)
fmt.Println(data)
[102 203]
[0 1 102 203 4 5]

cap与len

len – 来求长度,比如string、array、slice、map、channel ,返回长度
cap – capacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)

栗子

s1 := []int{0, 1, 2, 3, 8: 100} // 通过初始化表达式构造,可使用索引号。8:100的意思是 索引号8的值为100
fmt.Println(s1, len(s1), cap(s1))
s2 := make([]int, 6, 8) // 使用 make 创建,指定 len 和 cap 值。
fmt.Println(s2, len(s2), cap(s2))
s3 := make([]int, 6) // 省略 cap,相当于 cap = len。
fmt.Println(s3, len(s3), cap(s3))
[0 1 2 3 0 0 0 0 100] 9 9
[0 0 0 0 0 0] 6 8
[0 0 0 0 0 0] 6 6

append 元素的添加与删除

s := []int{0, 1, 2, 3}
s = append(s, 4)
fmt.Println(s)

i := 2
s = append(s[:i], s[i+1:]...)
fmt.Println(s)

需要注意:append :向 slice 尾部添加数据,返回新的 slice 对象。

s1 := make([]int, 0, 5)
fmt.Printf("%p\n", &s1)
s2 := append(s1, 1)
fmt.Printf("%p\n", &s2)
fmt.Println(s1, s2)
0xc0000987a0
0xc0000987c0
[] [1]

copy 切片拷贝

copy是内置函数,用于从切片中复制数据(存在一种特殊情况即从字符串中复制到字符切片中), 源切片和目的切片可以重叠,返回复制的元素个数,等于源和目的的最小长度值。

func copy(dst, src []Type) int
  1. 不同类型的切片无法复制
  2. 如果s1的长度大于s2的长度,将s2中对应位置上的值替换s1中对应位置的值
  3. 如果s1的长度小于s2的长度,多余的将不做替换
s1 := []int{1,2,3}
s2 := []int{4,5}
s3 := []int{6,7,8,9}
copy(s1,s2)
fmt.Println(s1)
fmt.Println(s2)
copy(s2,s3)
fmt.Println(s2)
fmt.Println(s3)

[4 5 3]
[4 5]
[6 7]
[6 7 8 9]

两个slice可指向同一底层数组,允许元素区间重叠如下

da := [...]int{0,1,2,3,4,5,6,7,8,9}
fmt.Println("原:",da)
da1 := da[7:9]
da2 := da[0:5]
fmt.Println("da1:",da1)
fmt.Println("da2:",da2)
copy(da2,da1)
fmt.Println("copy-da1:",da1)
fmt.Println("copy-da2:",da2)
fmt.Println("copy-da:",da)

原: [0 1 2 3 4 5 6 7 8 9]
da1: [7 8]
da2: [0 1 2 3 4]
copy-da1: [7 8]
copy-da2: [7 8 2 3 4]
copy-da: [7 8 2 3 4 5 6 7 8 9]

range 遍历

dt := [...]int{0,1,2,3,4,5,6,7,8,9}
for index, value := range dt {
	fmt.Printf("index-key:%v,value:%v\n",index,value)
}
index-key:0,value:0
index-key:1,value:1
index-key:2,value:2
index-key:3,value:3
index-key:4,value:4
index-key:5,value:5
index-key:6,value:6
index-key:7,value:7
index-key:8,value:8
index-key:9,value:9

其他操作

数组元素指针

sss := []int{0,1,2,3}
p := &sss[2]  // *int,获取底层数组元素指针
*p += 100
fmt.Println(sss)
[0 1 102 3]

多维元素

darr := [][]int{
	[]int{1,2,3},
	[]int{100,200},
	[]int{10,20,30,40},
}
fmt.Println(darr)
[[1 2 3] [100 200] [10 20 30 40]]

与结构体结合

type objfs struct {
	x int
	b string
}
func main() {
	d := [5]objfs{}
	d[0].x = 10
	d[0].b = "string"
	fmt.Println(d)
}
[{10 string} {0 } {0 } {0 } {0 }]

字符串和切片

str := "hello world"
sd1 := str[0:5]
fmt.Println(sd1)
sd2 := str[6:]
fmt.Println(sd2)
hello
world

解释:string底层就是一个byte的数组,因此,也可以进行切片操作。


string本身是不可变的,因此要改变string中字符。需要如下操作:

英文字符串
注意:append(sa, ‘ss’) 不能这样

std := "aka-ddz"
sa := []byte(std)  // uint8 类型
sa = append(sa, ' ')
sa = append(sa, 's')
sa = append(sa, 'a')
fmt.Println(sa)
std = string(sa)
fmt.Println(std)
[97 107 97 45 100 100 122 32 115 97]
aka-ddz sa

含有中文字符串的操作

str := "你好,世界!hello world!"
s := []rune(str)  //rune 代表一个UTF-8字符 rune 类型等价于 int32
s[3] = '够'
s[4] = '浪'
s[12] = 'g'
s = s[:14]
str = string(s)
fmt.Println(str)
你好,够浪!hello go

超出原 slice.cap 限制,就会重新分配底层数组,即便原数组并未填满

data := [...]int{0, 1, 2, 3, 4, 10: 0}
s := data[:2:3]
s = append(s, 100, 200) // 一次 append 两个值,超出 s.cap 限制。
fmt.Println(s, data) // 重新分配底层数组,与原数组无关。
fmt.Println(&s[0], &data[0]) // 比对底层数组起始指针
[0 1 100 200] [0 1 2 3 4 0 0 0 0 0 0]
0xc4200160f0 0xc420070060

从输出结果可以看出,append 后的 s 重新分配了底层数组,并复制数据。如果只追加一个值,则不会超过 s.cap 限制,也就不会重新分配。 通常以 2 倍容量重新分配底层数组。在大批量添加数据时,建议一次性分配足够大的空间,以减少内存分配和数据复制开销。或初始化足够长的 len 属性,改用索引号进行操作。及时释放不再使用的 slice 对象,避免持有过期数组,造成 GC 无法回收。

六、map

map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。

Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。

定义

map[KeyType]ValueType
KeyType:表示键的类型。
ValueType:表示键对应的值的类型。

map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:

make(map[KeyType]ValueType, [cap])
cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容量。

map基本使用

map中的数据都是成对出现的,map的基本使用示例代码如下:

// 定义map集合
var goClassMap map[string]string
goClassMap = make(map[string]string)
// 或
// goClassMap := make(map[string]string)
// 添加数据
goClassMap["teacher"] = "shineyork"
goClassMap["contents"] = "go"
fmt.Println(goClassMap)
// 输出
for index, value := range goClassMap {
	fmt.Printf("index : %v, value : %v \n", index, value)
}
map[contents:go teacher:shineyork]
index : contents, value : go
index : teacher, value : shineyork

map也支持在声明的时候填充元素,栗子:

userInfo := map[string]string{
	"username": "shineyork",
	"class": "go , php , java , python ,c++",
}
for index, value := range userInfo {
	fmt.Printf("index : %v, value : %v \n", index, value)
}
index : username, value : shineyork
index : class, value : go , php , java , python ,c++

判断某个键是否存在

Go语言中有个判断map中键是否存在的特殊写法,格式如下

userInfo := map[string]string{
	"username": "shineyork",
	"class": "go , php , java , python ,c++",
}
v, ok := userInfo["age"]
if ok {
	fmt.Println(v)
} else {
	fmt.Println("无字段")
}

遍历

与普通数组一样利用range,参考上面

delete()函数删除键值对

使用delete()内建函数从map中删除一组键值对,delete()函数的格式如下:

delete(map, key)
map:表示要删除键值对的map
key:表示要删除的键值对的键

栗子

userInfo := map[string]string{
	"username": "akaddz",
	"class": "go , php , java , python ,c++",
}
userInfo["age"] = "18"
fmt.Println(userInfo)

delete(userInfo, "age")
fmt.Println(userInfo)
map[age:18 class:go , php , java , python ,c++ username:akaddz]
map[class:go , php , java , python ,c++ username:akaddz]

与切片擦出火花

元素为map类型的切片

var mapSlice = make([]map[string]string, 3)
for index, value := range mapSlice {
	fmt.Printf("index:%d value:%v\n", index, value)
}
fmt.Println("after init")
// 对切片中的map元素进行初始化
mapSlice[0] = make(map[string]string, 10)
mapSlice[0]["name"] = "王五"
mapSlice[0]["password"] = "123456"
mapSlice[0]["address"] = "红旗大街"
for index, value := range mapSlice {
	fmt.Printf("index:%d value:%v\n", index, value)
}

值为切片类型的map

var sliceMap = make(map[string][]string, 3)
fmt.Println(sliceMap)
fmt.Println("after init")
key := "中国"
value, ok := sliceMap[key]
if !ok {
	value = make([]string, 0, 2)
}
value = append(value, "北京", "上海")
sliceMap[key] = value
fmt.Println(sliceMap)
// --
sliceMap["中国"] = make([]string, 0, 2)
sliceMap["中国"] = append(sliceMap["中国"], "北京", "上海")
fmt.Println(sliceMap)

fmt

fmt包: 实现了类似C语言printf和scanf的格式化I/O。主要分为向外输出内容和获取输入内容两大部分。

func main(){
	fmt.println(“hello go lang”)
}

fmt向外输出

函数作用实例
Print(a …interface{}) (n int, err error)打印不换行Print(“hello”)
Printf(format string, a …interface{}) (n int, err error)占位符打印不换行Printf(“%s go lang”,“hello”)
Println(a …interface{}) (n int, err error)打印换行Print(“hello”)

详解请参考

下一篇:结构体与面向对象 03
上一篇:go快速入门

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值