go语言基础练习

 : jank    : 0     : 377    : 2016-11-02 15:09  go

2016/10/26

1.if,for循环语句(for还能当php中的while使用)

package main
import "fmt"
func main(){
  sum := 10
  for sum < 20 {
   sum = sum +2
   if sum == 18 {
      fmt.Println("成年礼")
      continue
   }
   fmt.Println(sum)
 }
}


2.switch,函数调用

注:Go里面switch默认相当于每个case最后带有break,匹配成功后不会自动向下执行其他case,而是跳出整个switch, 但是可以使用fallthrough强制执行后面的case代码

package main
import "fmt"
func main(){
var janks int = 45
var hei string
fmt.Println(jank(janks, hei))
}
func jank(jank int, ai string) (string, azk) { 
switch jank{
case 1: ai = "a"
case 2: ai = "b"
case 3: ai = "c"
case 4: ai = "d"
case 5: ai = "e"
case 6: ai = "f"
default: ai = " i love you 
"
}
azk := 520
return 
}

3.range 使用

package main
import "fmt"
func main(){
jank := [5]int{1,2,3,4,5}
    for key, val := range jank { //key不写则只输出值
    fmt.Println("jank键", key, "的值为:", val)
    }
}


4.变参传递

package main
import "fmt"
func main(){
//varjanks []int
fmt.Println(bian("i", "love", "you", "jank"))
}  
func bian(jank ...string) string{ 
    for  k, val := range jank {
    fmt.Println("jank键", k, "的值为:", val)
    }
    return "ok"
}

5.指针传参

优点:

1.传指针使得多个函数能操作同一个对象。

2.传指针比较轻量级,只是传内存地址,我们可以用指针传递体积大的结构体

3.Go语言中channel,slice,map这三种类型的实现机制类似指针,所以可以直接传递,

 而不用取地址后传递指针。(注:若函数需改变slice的长度,则仍需要取地址传递指针)

package main
import "fmt"
func main(){
var a int = 15
var b string = "jank"
fmt.Println(zhizhen(&a, &b))
}
func zhizhen(x *int, y *string)(int, string){
return *x, *y
}


6.defer

Go语言中有种不错的设计,即延迟(defer)语句,你可以在函数中添加多个defer语句。

当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回.

package main
import "fmt"
func main(){
var a int = 15
var b string = "jank"
fmt.Println(zhizhen(&a, &b))
fmt.Println(a, b)
fmt.Println(a)
}
func zhizhen(x *int, y *string)(int, string){
defer fmt.Println("i love you") //倒序执行
if *x == 3 {
return 14, "jackson"
}
if *x == 15 {
*y = "jankai"
*x = 520 //给指针地址赋值
} 
fmt.Println("karen")
return *x, *y
}


7.函数当作值、类型传递(相当于自定义的结构体)

package main
import "fmt"
type testInt func(int) bool    //声明一个类型
func ai(ai int) bool {         //一个判断是否为int型的函数
return true
}
func main(){
ais := []int{15,52,12,23,14}
fmt.Println(jank(ais, ai)) //调用函数
}
func jank(x []int, f testInt) string { //调用函数
for k, v := range x{
if f(v){
fmt.Println("当期第", k, "个数字为:", v)
}
}
return "ok"
}

8.结构体

package main
import "fmt"
type Book struct {
    title string
    auth string
    age int
    from string
}
func main(){
var Book1 Book
Book1.title = "书法"
Book1.auth = "jank"
Book1.age = 2016
Book1.from = "北京"
fmt.Println(jank(Book1)) //调用函数
}
func jank(book Book) string { //调用函数
fmt.Println(book)
return "ok"
}


9.Panic和Recover

Go没有像Java那样的异常机制,它不能抛出异常,而是使用了panic和recover机制。

1.Panic是一个内建函数,可以中断原有的控制流程,进入一个令人恐慌的流程中。当函数F调用panic,

函数F的执行被中断,但是F中的延迟函数会正常执行,然后F返回到调用它的地方。在调用的地方,

F的行为就像调用了panic。这一过程继续向上,直到发生panic的goroutine中所有调用的函数返回,

此时程序退出。恐慌可以直接调用panic产生。也可以由运行时错误产生,例如访问越界的数组。

2.Recover是一个内建的函数,可以让进入令人恐慌的流程中的goroutine恢复过来。

recover仅在延迟函数中有效。在正常的执行过程中,调用recover会返回nil,并且没有其它任何效果

如果当前的goroutine陷入恐慌,调用recover可以捕获到panic的输入值,并且恢复正常的执行。


package main
func main(){
var user string
if user == "" {
        panic("no value for $USER")
    }
}


10.main函数和init函数

1.Go里面有两个保留的函数:init函数(能够应用于所有的package)和main函数(

只能应用于package main)这两个函数在定义时不能有任何的参数和返回值。

虽然一个package里面可以写任意多个init函数,但这无论是对于可读性还是以后的可维护性来说,

我们都强烈建议用户在一个package中每个文件只写一个init函数。

2.Go程序会自动调用init()和main(),所以你不需要在任何地方调用这两个函数。

每个package中的init函数都是可选的,但package main就必须包含一个main函数。

3.程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么就会在编译时将它们依次导入。

有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到fmt包,

但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,

那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数

(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行

初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。


11.struct的几种赋值方式

1.按照顺序提供初始化值:

P := person{"Tom", 25}


2.通过field:value的方式初始化,这样可以任意顺序:

P := person{age:24, name:"Tom"}


3.当然也可以通过new函数分配一个指针,此处P的类型为*person:

P := new(person)


12.struct的匿名函数

如:

package main
import "fmt"
type stu struct {
humen         //匿名函数
name string
age int
}
type humen struct {
name string 
age int
}
func main(){
h := stu{humen{"jank",23}, "jank", 23}
fmt.Println(h)
}


13.获取字段类型

package main
import (
f "fmt" //重命名导入的包
"reflect"
   )
type Book struct {
title string
author string
}
func main() {
var book Book
var booka string = "jankaga"
book.title = "书法"
book.author = "jank"
f.Println(reflect.TypeOf(booka))//获取类型
f.Println(Books(book))
}
func Books(books Book) Book {      //返回结构体类型
f.Println(reflect.TypeOf(books))
return books
}

2016/11/01

14.面向对象

1. 带有接收者的函数我们称为method,语法如下:

func (r ReceiverType) funcName(parameters) (results){


}


例:

计算面积

package main
import ( 
"fmt"
"math"
)
type chkuan struct {
chang float64
kuan float64
}
type ban struct {
banjing float64
}
func(l chkuan) area() float64 {
return l.chang * l.kuan
}
func(r ban) area() float64 {
return r.banjing * r.banjing * math.Pi
}
func main(){
c := chkuan{10, 20}
rd := ban{10}
fmt.Println(c.area())
fmt.Println(rd.area())
}

2. 如果一个method的receiver是*T,你可以在一个T类型的实例变量V上面调用这个method,而不需要&V去调用这个method

如果一个method的receiver是T,你可以在一个*T类型的变量P上面调用这个method,而不需要 *P去调用这个method

例:

package main
import "fmt"
const(
    WHITE = iota
    BLACK
    BLUE
    RED
    YELLOW
)
type Color byte
type Box struct { 
    width, height, depth float64
    color Color
}
type BoxList []Box //a slice of boxes
func (b Box) Volume() float64 {  //计算体积
    return b.width * b.height * b.depth
}
//设置颜色函数
func (b *Box) SetColor(c Color) { //接收指针参数,传递指针的时候不需要加"&",go会自行判断
    b.color = c
}
//最大颜色
func (bl BoxList) BiggestColor() Color {
    v := 0.00
    k := Color(WHITE)
    for _, b := range bl {
        if bv := b.Volume(); bv > v {
            v = bv
            k = b.color
        }
    }
    return k
}
//设置所有颜色
func (bl BoxList) PaintItBlack() {
    for i, _ := range bl {
        bl[i].SetColor(BLACK)
    }
}
func (c Color) String() string {
    strings := []string {"WHITE", "BLACK", "BLUE", "RED", "YELLOW"}
    return strings[c]
}
func main() {
    boxes := BoxList {
        Box{4, 4, 4, RED},
        Box{10, 10, 1, YELLOW},
        Box{1, 1, 20, BLACK},
        Box{10, 10, 1, BLUE},
        Box{10, 30, 1, WHITE},
        Box{20, 20, 20, YELLOW},
    }
    fmt.Printf("We have %d boxes in our set
", len(boxes))
    fmt.Println("The volume of the first one is", boxes[0].Volume(), "cm³")
    fmt.Println("The color of the last one is",boxes[len(boxes)-1].color.String())
    fmt.Println("The biggest one is", boxes.BiggestColor().String())
    fmt.Println("Let's paint them all black")
    boxes.PaintItBlack()
    fmt.Println("The color of the second one is", boxes[1].color.String())
    fmt.Println("Obviously, now, the biggest one is", boxes.BiggestColor().String())
    boxes[1].SetColor(WHITE) //设置slice中第二个的颜色为白色
    fmt.Println("当前颜色为", boxes[1].color.String())
    fmt.Println("当前颜色为", boxes[2].color.String())
    fmt.Println("当前颜色为", boxes[3].color.String())
    fmt.Println("当前颜色为", boxes[4].color.String())
}

3. 接口

package main
import "fmt"
type Human struct {
    name string
    age int
    phone string
}
type Student struct {
    Human //匿名字段
    school string
    loan float32
}
type Employee struct {
    Human //匿名字段
    company string
    money float32
}
//Human实现SayHi方法
func (h Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s
", h.name, h.phone)
}
//Human实现Sing方法
func (h Human) Sing(lyrics string) {
    fmt.Println("La la la la...", lyrics)
}
//Employee重载Human的SayHi方法
func (e Employee) SayHi() {
    fmt.Printf("Hi, I am %s, I work at %s. Call me on %s
", e.name,
        e.company, e.phone)
    }
// Interface Men被Human,Student和Employee实现
// 因为这三个类型都实现了这两个方法
type Men interface {
    SayHi()
    Sing(lyrics string)
}
func main() {
    mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
    paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
    sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
    tom := Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000}
    //定义Men类型的变量i
    var i Men
    //i能存储Student
    i = mike
    fmt.Println("This is Mike, a Student:")
    i.SayHi()
    i.Sing("November rain")
    //i也能存储Employee
    i = tom
    fmt.Println("This is tom, an Employee:")
    i.SayHi()
    i.Sing("Born to be wild")
    //定义了slice Men
    fmt.Println("Let's use a slice of Men and see what happens")
    x := make([]Men, 3)
    //这三个都是不同类型的元素,但是他们实现了interface同一个接口
    x[0], x[1], x[2] = paul, sam, mike
    for _, value := range x{
        value.SayHi()
    }
}

2016/11/02

15.并发

1. goroutine是Go并行设计的核心。goroutine说到底其实就是线程,但是它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现了这些goroutine之间的内存共享。执行goroutine只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。goroutine是通过Go的runtime管理的一个线程管理器。goroutine通过go关键字实现了,其实就是一个普通的函数。

package main
import (
    "fmt"
    "runtime"
)
func say(s string) {
    for i := 0; i < 5; i++ {
        runtime.Gosched()  
        //runtime.Gosched()表示让CPU把时间片让给别人,下次某个时候继续恢复执行该goroutine。
        fmt.Println(s)
    }
}
func main() {
    go say("world") //开一个新的Goroutines执行
    say("hello") //当前Goroutines执行
}

2. channel

1. goroutine之间如何进行数据的通信呢,Go提供了一个很好的通信机制channel。channel可以与Unix shell 中的双向管道做类比:可以通过它发送或者接收值。这些值只能是特定的类型:channel类型。定义一个channel时,也需要定义发送到channel的值的类型。注意,必须使用make 创建channel:

例:

ci := make(chan int)

cs := make(chan string)

cf := make(chan interface{})

2. channel通过操作符<-来接收和发送数据

例:

ch <- v    // 发送v到channel ch.

v := <-ch  // 从ch中接收数据,并赋值给v

package main
import "fmt"
func sum(a []int, c chan int) {
    total := 0
    for b := range a {
        total += b
    }
    c <- total //send c to chan
}
func main(){
    jank := []int{1, 2, 3, 4, 5, 6}
    c := make(chan int)
    go sum(jank, c)
    go sum(jank[len(jank)/2:], c)
    x, y := <-c, <-c //receive from c
    fmt.Println(x, y, x+y)
}

3. buffers channel

ch := make(chan type, value) //书写语法

例:

   ch:= make(chan bool, 4),创建了可以存储4个元素的bool 型channel。在这个channel 中,前4个元素可以无阻塞的写入。当写入第5个元素时,代码将会阻塞,直到其他goroutine从channel 中读取一些元素,腾出空间。

    package main
import "fmt"
func main() {
    c := make(chan int, 2)//修改2为1就报错,修改2为3可以正常运行
    c <- 1
    c <- 2
    fmt.Println(<-c)
    fmt.Println(<-c)
}
    //修改为1报如下的错误:
    //fatal error: all goroutines are asleep - deadlock!

3.range

可以通过range,像操作slice或者map一样操作缓存类型的channel

例:

package main
import (
    "fmt"
)
func fibonacci(n int, c chan int) {
    x, y := 1, 1
    for i := 0; i < n; i++ {
        c <- x
        x, y = y, x + y
    }
    close(c) //关闭channel,应在生产者关闭,不能再消费者关闭,否者引起panic,且不需要经常关闭,只有当确认没有任何数据发送的时候才关闭
}
func main() {
    c := make(chan int, 10)
    go fibonacci(cap(c), c)  //cap获取最大容量
    for i := range c {    //类似取数组一般,接收消息
        fmt.Println(i)
    }
}


4. 通过select可以监听channel上的数据流动。select默认是阻塞的,只有当监听的channel中有发送或,接收可以进行时才会运行,当多个channel都准备好的时候,select是随机的选择一个执行的。

package main
import "fmt"
func fibonacci(c, quit chan int) {
    x, y := 1, 1
    for {
        select {
        case c <- x:
            x, y = y, x + y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}
func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

5.超时

当阻塞的时候利用select设置超时

func main() {
    c := make(chan int)
    o := make(chan bool)
    go func() {
        for {
            select {
                case v := <- c:
                    println(v)
                case <- time.After(5 * time.Second):
                    println("timeout")
                    o <- true
                    break
            }
        }
    }()
    <- o
}


总结:

var和const参考2.2Go语言基础里面的变量和常量申明

package和import已经有过短暂的接触

func 用于定义函数和方法

return 用于从函数返回

defer 用于类似析构函数

go 用于并发

select 用于选择不同类型的通讯

interface 用于定义接口,参考2.6小节

struct 用于定义抽象数据类型,参考2.5小节

break、case、continue、for、fallthrough、else、if、switch、goto、default这些参考2.3流程介绍里面

chan用于channel通讯

type用于声明自定义类型

map用于声明map类型数据

range用于读取slice、map、channel数据


   

备案编号:赣ICP备15011386号

联系方式:qq:1150662577    邮箱:1150662577@qq.com