Golang极简入门教程(一):基本概念
安装Golang
在http://golang.org/dl/可以下载到Golang。安装文档:http://golang.org/doc/install。
HelloGo
我们先创建一个文件hello.go:
packagemain
import"fmt"
funcmain(){
fmt.Printf("helloGolang\n");
}
执行此程序:
gorunhello.go
包
Golang程序由包(packages)组成,程序从main包开始运行:
packagemain
此语句表示此文件属于main包(多个源文件可以属于同一个包)。import语句后接上包所在的路径(被叫做包路径或导入路径),一个目录中放置一个包,通常的做法是,目录名和包名相同:
import( "fmt" "math/rand" )
这里的“fmt”和“math/rand”为包路径(导入路径)。上面的import语句也可以这样写:
import"fmt" import"math/rand"
我们导入了包之后,就可以通过“包名.name”来引用导出的name了,例如:
import"fmt"
//fmt包导出了Printf
fmt.Printf("helloGolang\n");
在Golang中,一个名字如果首字母大写则表示此名字被导出。
函数
packagemain
import"fmt"
funcadd(xint,yint)int{
returnx+y
}
funcmain(){
fmt.Println(add(42,13))
}
需要注意的就是,变量名在类型之前,这和很多语言都不一样。另外xint,yint也可以写为x,yint:
funcadd(x,yint)int{
returnx+y
}
函数可以返回多个值:
packagemain
import"fmt"
funcswap(x,ystring)(string,string){
returny,x
}
funcmain(){
a,b:=swap("hello","world")
fmt.Println(a,b)
}
返回值可以被指定变量名,并且像变量一样使用:
packagemain
import"fmt"
funcsplit(sumint)(x,yint){
x=sum*4/9
y=sum-x
return
}
funcmain(){
fmt.Println(split(17))
}
可以看到split函数直接使用return语句而不用带参数。
变量
变量的声明使用var语句:
variint varc,python,javabool
变量在声明时可以进行初始化:
varx,yint=1,2 vari,j=true,"hello"
我们看到,初始化时可以指定也可以不指定变量类型。
按照Golang的语法,在函数外的任何结构(construct)都通过一个关键字开始,例如变量使用var关键字开始,函数使用func关键字开始。在函数内,变量赋值可以使用:=操作符:
packagemain
funcmain(){
varx,yint=1,2
i,j:=true,"hello"
}
:=操作符左边为变量,右边为值。
数据类型
基本数据类型:
1.bool
2.string
3.intint8int16int32int64
4.uintuint8uint16uint32uint64
5.uintptr
6.byte(等价于uint8)
7.rune(等价于int32,用于表示一个unicodecodepoint)
8.float32float64
9.complex64complex128
类型转换使用表达式T(v),含义为将v转换为类型T:
variint=42 varffloat64=float64(i) i:=42 f:=float64(i)
类型转换总需要显式的进行。
使用const来声明常量:
constPi=3.14 const( Big=1<<100 Small=Big>>99 )
控制语句
for语句
Golang使用(且只使用)for来进行循环(没有while语句):
packagemain
funcmain(){
sum:=0
fori:=0;i<10;i++{
sum+=i
}
//这种写法等价于C/C++等语言中的while语句
forsum<1000{
sum+=sum
}
}
区别于C/C++等语言,使用for语句时不需要()并且{}是必须的(后面谈到的if、switch在此语法处理上也是一样的)。如果需要无限循环,那么使用:
for{
}
if语句
if语句可以在执行条件判断前带一个语句(这常被叫做if带上一个短语句),此语句中变量的生命周期在if语句结束后结束。例如:
packagemain
import(
"fmt"
"math/rand"
)
funcmain(){
ifn:=rand.Intn(6);n<=2{
fmt.Println("[0,2]",n)
}else{
fmt.Println("[3,5]",n)
}
//这里开始无法使用变量n
}
switch
packagemain
import(
"fmt"
"runtime"
)
funcmain(){
fmt.Print("Gorunson")
//switch类似if可以带上一个短语句
switchos:=runtime.GOOS;os{
case"darwin":
fmt.Println("OSX.")
case"linux":
fmt.Println("Linux.")
default:
//freebsd,openbsd,
//plan9,windows...
fmt.Printf("%s.",os)
}
}
不像C/C++等语言,Golang中无需使用break语句来跳出switch。另外,switch可以没有条件:
packagemain
import(
"fmt"
"time"
)
funcmain(){
t:=time.Now()
switch{
caset.Hour()<12:
fmt.Println("Goodmorning!")
caset.Hour()<17:
fmt.Println("Goodafternoon.")
default:
fmt.Println("Goodevening.")
}
}
defer
一个defer语句能够将一个函数调用加入一个列表中(这个函数调用被叫做deferred函数调用),在当前函数调用结束时调用列表中的函数。范例:
funcCopyFile(dstName,srcNamestring)(writtenint64,errerror){
src,err:=os.Open(srcName)
iferr!=nil{
return
}
defersrc.Close()
dst,err:=os.Create(dstName)
iferr!=nil{
return
}
deferdst.Close()
returnio.Copy(dst,src)
}
deferred函数调用按先进后出的顺序执行:
packagemain
import"fmt"
funcmain(){
fori:=0;i<5;i++{
//输出43210
deferfmt.Print(i)
}
}
结构(structs)
结构是一个域的集合:
packagemain
import"fmt"
typeVertexstruct{
Xint
Yint
}
funcmain(){
v:=Vertex{1,2}
v.X=4
fmt.Println(v)
}
Golang中是存在指针的,但是指针不支持算术运算:
p:=Vertex{1,2}//{1,2}为structliteral
q:=&p//q类型为*Vertex
q.X=2//直接访问域X
就像上面看到的,struct的literal由{}包裹,在structliteral中我们可以使用Name:这样的语法来为特定域设置值:
typeVertexstruct{
X,Yint
}
r:=Vertex{X:3}//这时候Y为0
new函数
我们可以通过表达式new(T)分配一个被初始化为0且类型为T的值,并且返回指向此值的指针,用法如下:
varp*T=new(T) p:=new(T)
更详尽的例子:
packagemain
import"fmt"
typeVertexstruct{
X,Yint
}
funcmain(){
v:=new(Vertex)
fmt.Println(v)
v.X,v.Y=11,9
fmt.Println(v)
}
数组和slice
[n]T在Golang中是一个类型(就像*T一样),表示一个长度为n的数组其元素类型为T。范例:
packagemain
import"fmt"
funcmain(){
vara[2]string
a[0]="Hello"
a[1]="World"
fmt.Println(a[0],a[1])
fmt.Println(a)
}
注意,数组长度无法被改变。
slice是一个数据结构,其指向一个数组某个连续的部分。slice用起来很像数组。[]T为slice类型,其中元素类型为T:
packagemain
import"fmt"
funcmain(){
//构建一个slice
p:=[]int{2,3,5,7,11,13}
fmt.Println("p==",p)
fori:=0;i<len(p);i++{
fmt.Printf("p[%d]==%d\n",i,p[i])
}
}
表达式s[lo:hi]用于创建一个slice,新创建的slice的元素为s中[lo,hi)位置的元素。
创建slice使用make函数(而不是用new函数,因为需要设置额外的参数来控制slice的创建):
//len(a)为5 a:=make([]int,5)
这里make函数会创建一个数组(其元素初始化为0)并返回一个slice指向此数组。make可以带第三个参数,用于指定容量:
//len(b)为0 //cap(b)为5 b:=make([]int,0,5) b=b[:cap(b)]//len(b)=5,cap(b)=5 b=b[1:]//len(b)=4,cap(b)=4
一个没有值的slice是nil,长度和容量都为0。
packagemain
import"fmt"
funcmain(){
varz[]int
fmt.Println(z,len(z),cap(z))
ifz==nil{
fmt.Println("nil!")
}
}
range
range被用在for中来迭代一个slice或者一个map:
packagemain
import"fmt"
vars=[]int{1,2,3}
funcmain(){
fori,v:=ranges{
fmt.Println(i,v)
}
//只需要值,使用_忽略索引
for_,v:=ranges{
fmt.Println(v)
}
//只需要索引
fori:=ranges{
fmt.Println(i)
}
}
map
map用于映射key到value(值)。map可以通过make来创建(而非new):
packagemain
import"fmt"
typeVertexstruct{
Lat,Longfloat64
}
varmmap[string]Vertex
funcmain(){
m=make(map[string]Vertex)
m["BellLabs"]=Vertex{
40.68433,-74.39967,
}
fmt.Println(m["BellLabs"])
}
mapiteral很像structliteral:
varm=map[string]Vertex{
//这里Vertex可以省略不写
"BellLabs":Vertex{
40.68433,-74.39967,
},
"Google":Vertex{
37.42202,-122.08408,
},
}
使用[]来访问map中的值,使用delete来删除map中的值:
m[key]=elem elem=m[key] delete(m,key)
如果需要检查map中某key是否存在使用:
elem,ok=m[key]
elem表示key的值(key不存在时,elem为0),ok表示key是否存在。
闭包
Golang中函数也是一个值(就像int值一样),且函数可以是一个闭包。闭包是一个引用了外部变量的函数。看一个例子:
packagemain
import"fmt"
funcadder()func(int)int{
sum:=0
//返回一个闭包,此闭包引用了外部变量sum
returnfunc(xint)int{
sum+=x
returnsum
}
}
funcmain(){
a:=adder()
fmt.Println(a(9527))
}