golang入门(1)

1、hello world
在Go 指南中,用一个传统的方式展现了Go:让它打印“Hello World”

package main
import “fmt” // 实现格式化的 I/O。
/ Print something /
func main() {
fmt.Printf(“Hello, world; or   ó; orこんにちは世界\n”)
}

逐行阅读这个程序。
首行这个是必须的。所有的Go文件以package
开头,对于独立运行的执行文件必须是package main;
这是说需要将”fmt”包加入main。不是main的其他包都被称为库,其他许多编
程语言有着类似的概念(参阅第4 章)。末尾以// 开头的内容是注释;
这同样是注释,不过这是被包裹于// 之间的;
package main 必须首先出现,紧跟着是import。在Go 中,package 总是首先出现,然后是import,然后是其他所有内容。当Go 程序在执行的时候,首先调用的函数是main.main(),这是从C 中继承而来。这里定义了这个函数;
第8行调用了来自于fmt包的函数打印字符串到屏幕。字符串由”包裹,并且可以包含非ASCII 的字符。这里使用了希腊文和日文。

构建Go 程序的最佳途径是使用go 工具。
构建helloworld 只需要:
% go build helloworld.go
结果是叫做helloworld 的可执行文件。
% ./helloworld
Hello, world; or   ó; or こんにちは世界

2、变量、类型和保留字
如果你希望将两个(或更多)语句放在一行书写,它们必须用分号(’;’) 分隔。一般情况下,你不需要分号。
Go 同其他语言不同的地方在于变量的类型在变量名的后面。不是:int a,而是a int。当定义了一个变量,它默认赋值为其类型的null 值。这意味着,在var a int后,a 的值为0。而var s string,意味着s 被赋值为零长度字符串,也就是””。
下面是两种赋值方式
var a int
var b bool
a = 15
b = false

a := 15
b := false
在左边使用了保留字var 声明变量,然后赋值给它。右边的代码使用了:= 使得在一步内完成了声明和赋值(这一形式只可用在函数内)。在这种情况下,变量的类型是由值推演出来的。值15 表示是int 类型,值false 告诉Go 它的类型应当是bool。
多个var 声明可以成组,const 和import 同样允许这么做。留意圆括号的使用:
var (
x int
b bool
)
有相同类型的多个变量同样可以在一行内完成声明: var x, y int 让x 和y 都是int 类型变量。同样可以使用平行赋值:
a, b := 20, 16
一个特殊的变量名是_(下划线)。任何赋给它的值都被丢弃。在这个例子中,将35 赋值给b,同时丢弃34。
_, b := 34, 35
Go 的编译器对声明却未使用的变量在报错。

1.布尔类型
布尔类型表示由预定义的常量true 和false 代表的布尔判定值。布尔类型是bool。

2.数字类型
Go 有众所周知的类型如int,这个类型根据你的硬件决定适当的长度。意味着在32 位硬件上,是32 位的;在64 位硬件上是64 位的。注意:int 是32 或64 位之一,不会定义成其他值。uint 情况相同。
如果你希望明确其长度,你可以使用int32 或者uint32。完整的整数类型列表(符号和无符号)是int8,int16,int32,int64 和byte,uint8,uint16,uint32,uint64。byte 是uint8 的别名。浮点类型的值有float32 和float64 (没有float 类型)。64 位的整数和浮点数总是64 位的,即便是在32 位的架构上。
需要留意的是这些类型全部都是独立的,并且混合用这些类型向变量赋值会引起编译器错误,例如下面的代码:
package main 1
func main() { 3
var a int Generic integer type 4
var b int32 32 bits integer type 5
a = 15 6
b = a + a Illegal mixing of these types 7
b = b + 5 5 is a (typeless) constant, so this is OK 8
}
3.常量
常量在Go 中,也就是constant。它们在编译时被创建,只能是数字、字符串或布尔值; const x = 42 生成x 这个常量。可以使用iota b 生成枚举值。
const (
a = iota
b = iota
)
第一个iota 表示为0,因此a 等于0,当iota 再次在新的一行使用时,它的值增加了1,因此b 的值是1。
也可以像下面这样,省略Go 重复的= iota:
const (
a = iota
b Implicitly b = iota
)
如果需要,可以明确指定常量的类型:
const (
a = 0 Is an int now
b string = “0”
)

4.字符串
另一个重要的内建类型是string。赋值字符串的例子:
s := “Hello World!”
字符串在Go 中是UTF-8 的由双引号(”)包裹的字符序列。如果你使用单引号(’)则表示一个字符(UTF-8 编码)——这种在Go 中不是string。
一旦给变量赋值,字符串就不能修改了:在Go 中字符串是不可变的。从C 来的用户,下面的情况在Go 中是非法的。
var s string = “hello”
s[0] = ‘c’ 修改第一个字符为’c’,这会报错
在Go 中实现这个,需要下面的方法:
s := “hello”
c := []rune(s)
c[0] = ‘c’
s2 := string(c)
fmt.Printf(“%s\n”, s2)
转换s 为rune 数组,查阅在第5 章”转换” 节、63 页的内容;
修改数组的第一个元素;
创建新的字符串s2 保存修改;
用fmt.Printf 函数输出字符串。
多行字符串:
行字符串。如果这样写:
s := “Starting part”

  • “Ending part”
    会被转换为:
    s := “Starting part”;
  • “Ending part”;
    这是错误的语法,应当这样写:
    s := “Starting part” +
    “Ending part”
    Go 就不会在错误的地方插入分号。另一种方式是使用反引号作为原始字符串符号: s :=Starting part
    Ending part`
    留意最后一个例子s 现在也包含换行。不像转义字符串标识,原始字符串标识的值在引号内的字符是不转义的。

5.rune
Rune 是int32 的别名。用UTF-8 进行编码。这个类型在什么时候使用呢?例如需要遍历字符串中的字符。可以循环每个字节(仅在使用US ASCII 编码字符串时与字符等价,而它们在Go 中不存在!)。因此为了获得实际的字符,需要使用rune 类型。

6.复数
Go 原生支持复数。它的变量类型是complex128 (64 位虚数部分)。如果需要小一些的,还有complex64 – 32 位的虚数部分。复数写为re + imi,re 是实数部分,im 是虚数部分,而i 是标记’i’ 。使用复数的一个例子:
var c complex64 = 5+5i;fmt.Printf(“Value is: %v”, c)
将会打印:(5+5i)

7.错误
任何足够大的程序或多或少都会需要使用到错误报告。因此Go 有为了错误而存在的内建类型,叫做error。var a error 定义了a 为一个error,a 的值是nil。

3、运算符和内建函数

      • / 和% 会像你期望的那样工作, & | ^ 和&^ 分别表示位运算符按位与, 按位或,按位异或和位清除。&& 和|| 运算符是逻辑与和逻辑或。
        虽然Go 不支持运算符重载(或者方法重载),而一些内建运算符却支持重载。例如+ 可以用于整数、浮点数、复数和字符串(字符串相加表示串联它们)。

4、Go 保留字
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

5、控制结构
在Go 中只有很少的几个控制结构。例如这里没有do 或者while 循环,只有for。有(灵活的)switch 语句和if,而switch 接受像for 那样可选的初始化语句。还有叫做类型选择和多路通讯转接器的select(参阅第7 章)。语法有所不同(同C 相比):无需圆括号,而语句体必须总是包含在大括号内。

1.if/eles
在Go 中if 看起来是这样的:
if x > 0 { //{ is mandatory
return y
} else {
return x
}
强制大括号鼓励将简单的if 语句写在多行上。无论如何,这都是一个很好的形式,尤其是语句体中含有控制语句,例如return 或者break。
if 和switch 接受初始化语句,通常用于设置一个(局部)变量。
if err := file.Chmod(0664); err != nil { //nil is like C’s NULL
log.Stderr(err) //Scope of err is limited to if’s body
return err
}
在Go 库中,你会发现当一个if 语句不会进入下一个语句流程– 也就是说,语句体结束于break,continue,goto 或者return – 不必要的else 会被省略。
f, err := os.Open(name, os.O_RDONLY, 0)
if err != nil {
return err
}
doSomething(f)

2.goto
Go 有goto 语句——明智的使用它。用goto 跳转到一定是当前函数内定义的标签。例如假设这样一个循环:
func myfunc() {
i := 0
Here: //这行的第一个词,以分号结束作为标签,标签名是大小写敏感的。
println(i)
i++
goto Here 跳转
}

3.for
Go 的for 循环有三种形式,只有其中的一种使用分号。
for init; condition; post { } //和C 的for 一样
for condition { } //和while 一样
for { } //和C 的for(;;) 一样(死循环)
短声明使得在循环中声明一个序号变量更加容易。
sum := 0
for i := 0; i < 10; i++ {
sum += i //sum = sum + i 的简化写法
} //i 实例在循环结束会消失
最后,由于Go 没有逗号表达式,而++ 和– 是语句而不是表达式,如果你想在for 中执行多个变量,应当使用平行赋值。
// Reverse a
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 { //平行赋值
a[i], a[j] = a[j], a[i] //这里也是
}

4.break 和continue
利用break 可以提前退出循环,break 终止当前的循环。
for i := 0; i < 10; i++ {
if i > 5 {
break //终止这个循环,只打印0 到5
}
println(i)
}
循环嵌套循环时,可以在break 后指定标签。用标签决定哪个循环被终止:
J: for j := 0; j < 5; j++ {
for i := 0; i < 10; i++ {
if i > 5 {
break J //现在终止的是j 循环,而不是i 的那个
}
println(i)
}
}
5.range
保留字range 可用于循环。它可以在slice、array、string、map 和channel。range 是个迭代器,当被调用的时候,从它循环的内容中返回一个键值对。基于不同的内容,range 返回不同的东西。
当对slice 或者array 做循环时,range 返回序号作为键,这个序号对应的内容作为值。考虑这个代码:
list := []string{“a”, “b”, “c”, “d”, “e”, “f”}
for k, v := range list {
// 对 k 和 v 做想做的事情
}
创建一个字符串的slice
用range对其进行循环。每一个迭代,range将返回int类型的序号,string类型的值,以0 和“a” 开始。
k 的值为0…5,而v 在循环从“a”…“f”。
也可以在字符串上直接使用range。这样字符串被打散成独立的Unicode 字符并且起始位按照UTF-8 解析。循环:
for pos, char := range “a发x” {
fmt.Printf(“character ‘%c’ starts at byte position %d\n”, char, pos)
}
打印:
character ‘a’ starts at byte position 0
character ‘发’ starts at byte position 1
character ‘x’ starts at byte position 4 //发took 3 bytes

6.switch
Go 的switch 非常灵活。表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项,而如果switch 没有表达式,它会匹配true 。这产生一种可能——使用switch 编写if-else-if-else 判断序列。
func unhex(c byte) byte {
switch {
case ‘0’ <= c && c <= ‘9’:
return c - ‘0’
case ‘a’ <= c && c <= ‘f’:
return c - ‘a’ + 10
case ‘A’ <= c && c <= ‘F’:
return c - ‘A’ + 10
}
return 0
}
它不会匹配失败后自动向下尝试,但是可以使用fallthrough 使其这样做。没有fallthrough:
switch i {
case 0: // 空的case 体
case 1:
f() // 当i == 0 时,f 不会被调用!
}
而这样:
switch i {
case 0: fallthrough
case 1:
f() // 当i == 0 时,f 会被调用!
}
用default 可以指定当其他所有分支都不匹配的时候的行为。
switch i {
case 0:
case 1:
f()
default:
g() // 当i 不等于0 或1 时调用
}
分支可以使用逗号分隔的列表。
func shouldEscape(c byte) bool {
switch c {
case ‘ ‘, ‘?’, ‘&’, ‘=’, ‘#’, ‘+’: , as “or”
return true
}
return false
}
这里有一个使用两个switch 对字节数组进行比较的例子:
// 比较返回两个字节数组字典数序先后的整数。
// 如果a == b 返回0,如果a < b 返回-1,而如果a > b 返回+1
func Compare(a, b []byte) int {
for i := 0; i < len(a) && i < len(b); i++ {
switch {
case a[i] > b[i]:
return 1
case a[i] < b[i]:
return -1
}
}
// 长度不同,则不相等
switch {
case len(a) < len(b):
return -1
case len(a) > len(b):
return 1
}
return 0 // 字符串相等
}

6、内建函数
预定义了少数函数,这意味着无需引用任何包就可以使用它们。
close new panic complex
delete make recover real
len append print imag
cap copy println

close 用于channel 通讯。使用它来关闭channel
delete 用于在map 中删除实例。
len 和cap 可用于不同的类型, len 用于返回字符串、slice 和数组的长度.
new 用于各种类型的内存分配。
make 用于内建类型(map、slice 和channel)的内存分配。
copy 用于复制slice
append 用于追加slice。
panic 和recover 用于异常处理机制
print 和println 是底层打印函数,可以在不引入fmt 包的情况下使用。
complex、real 和imag 全部用于处理复数

7、array、slices 和map
可以利用array 在列表中进行多个值的排序,或者使用更加灵活的:slice。字典或哈希类型同样可以使用,在Go 中叫做map。

1.array
array 由[n] 定义, n 标示array 的长度,而 标示希望存储的内容的类型。对array 的元素赋值或索引是由方括号完成的:
var arr [10]int
arr[0] = 42
arr[1] = 13
fmt.Printf(“The first element is %d\n”, arr[0])

像var arr = [10]int 这样的数组类型有固定的大小。大小是类型的一部分。由于不同的大小是不同的类型,因此不能改变大小。
数组同样是值类型的:将一个数组赋值给另一个数组,会复制所有的元素。尤其是当向函数内传递一个数组的时候,它会获得一个数组的副本,而不是数组的指针。

可以像这样声明一个数组:var a [3]int,如果不使用零来初始化它,则用复合声明:a := [3]int{1, 2, 3} 也可以简写为a := […]int{1, 2, 3},Go 会自动统计元素的个数。注意,所有项目必须都指定。因此,如果你使用多维数组,有一些内容你必须录入:
a := [2][2]int{ [2]int{1,2}, [2]int{3,4} }
类似于:
a := [2][2]int{ […]int{1,2}, […]int{3,4} }
上面的例子可以修改为:
a := [2][2]int{ {1,2}, {3,4} }

2.slice
slice 与array 接近,但是在新的元素加入的时候可以增加长度。slice 总是指向底层的一个array。slice 是一个指向array 的指针,这是其与array 不同的地方;slice 是引用类型, 这意味着当赋值某个slice 到另外一个变量,两个引用会指向同一个array。
例如,如果一个函数需要一个slice 参数,在其内对slice 元素的修改也会体现在函数调用者中,这和传递底层的array 指针类似。通过:
sl := make([]int, 10)
创建了一个保存有10 个元素的slice。需要注意的是底层的array 并无不同。slice 总是与一个固定长度的array 成对出现。其影响slice 的容量和长度。

// array[n:m] 从array 创建了一个slice,具有元素n 到m-1
a := […]int{1, 2, 3, 4, 5}
s1 := a[2:4]
s2 := a[1:5]
s3 := a[:]
s4 := a[:4]
s5 := s2[:]
定义一个5个元素的array,序号从0到4;
array、slices 和map 21
从序号2至3创建slice,它包含元素3, 4;
从序号1至4创建,它包含元素2, 3, 4, 5;
用array中的所有元素创建slice,这是a[0:len(a)] 的简化写法;
从序号0至3创建,这是a[0:4]的简化写法,得到1, 2, 3, 4;
从slice s2 创建slice,注意s5 仍然指向array a。

如果你想要扩展slice,有一堆内建函数让你的日子更加好过一些: append和copy。
函数append 向slice s 追加零值或其他x 值,并且返回追加后的新的、与s 有相同类型的slice。如果s 没有足够的容量存储追加的值,append 分配一个足够大的、新的slice 来存放原有slice 的元素和追加的值。因此,返回的slice 可能指向不同的底层array。

s0 := []int{0, 0}
s1 := append(s0, 2)
s2 := append(s1, 3, 5, 7)
s3 := append(s2, s0…)
追加一个元素,s1 == []int{0, 0, 2};
追加多个元素,s2 == []int{0, 0, 2, 3, 5, 7};
追加一个slice,s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}。注意这三个点!
还有函数copy 从源slice src 复制元素到目标dst,并且返回复制的元素的个数。源和目标可能重叠。复制的数量是len(src) 和len(dst) 中的最小值。
var a = […]int{0, 1, 2, 3, 4, 5, 6, 7}
var s = make([]int, 6)
n1 := copy(s, a[0:]) //n1 == 6, s == []int{0, 1, 2, 3, 4, 5}
n2 := copy(s, s[2:]) //n2 == 4, s == []int{2, 3, 4, 5, 4, 5}

3.map
许多语言都内建了类似的类型,例如Perl 有哈希,Python 有字典,而C++ 同样也有map(作为库)。在Go 中有map 类型。map 可以认为是一个用字符串做索引的数组(在其最简单的形式下)。下面定义了map 类型,用于将string(月的缩写)转换为int – 那个月的天数。一般定义map 的方法是:map[]
monthdays := map[string]int{
“Jan”: 31, “Feb”: 28, “Mar”: 31,
“Apr”: 30, “May”: 31, “Jun”: 30,
“Jul”: 31, “Aug”: 31, “Sep”: 30,
“Oct”: 31, “Nov”: 30, “Dec”: 31, //逗号是必须的
}

留意,当只需要声明一个map 的时候,使用make 的形式: monthdays := make(map[string]int)
当在map 中索引(搜索)时,使用方括号。例如打印出12 月的天数:
fmt.Printf(“%d\n”, monthdays[“Dec”])

当对array、slice、string 或者map 循环遍历的时候,range 会帮助你,每次调用,它都会返回一个键和对应的值。
year := 0
for , days := range monthdays { //键没有使用,因此用, days
year += days
}
fmt.Printf(“Numbers of days in a year: %d\n”, year)
向map 增加元素,可以这样做:
monthdays[“Undecim”] = 30 //添加一个月
monthdays[“Feb”] = 29 //闰年时重写这个元素
检查元素是否存在,可以使用下面的方式:
var value int
var present bool
value, present = monthdays[“Jan”] //如果存在,present 则有值true
//或者更接近Go 的方式
v, ok := monthdays[“Jan”] //“逗号ok”形式
也可以从map 中移除元素:
delete(monthdays, “Mar”) //删除”Mar” 吧,总是下雨的月份
通常来说语句delete(m, x) 会删除map 中由m[x] 建立的实例。