Go语言学习笔记(五)

这是Go语言学习笔记的第五篇

条件语句

条件语句格式:

if condition {
    statement
} else {
    statement
}

几点注意的地方:

  • 不需要使用括号()将条件包含起来

  • 花括号{}必须存在

  • 左花括号{必须与if或else处于同一行

  • if之后,条件语句之前可以添加变量初始化语句, 使用;间隔

  • 在有返回值的函数中,要么每个分支都有 return 语句, 要么最终函数尾带有 return 语句.

选择语句

选择语句格式:

switch i {
    case 0:
        fmt.Println("0")
    case 1:
        fmt.Println("1")
    case 2:
        fmt.Println("2")
    case 3:
        fallthrough
    default:
        fmt.Println("default")
}

switch几点注意的地方:

  • switch后面的条件表达式不是必需的。

  • 左花括号{必需与switch处于同一行

  • switch的条件表达式不限制为常量或者整数

  • 单个case中可以有多个结果选项

  • 不需要用break来明确退出一个case

  • case后面明确添加fallthrough关键字,则会继续执行紧跟的下一个case

循环语句

Go语言中循环语句只支持for关键字,不支持whiledo-while。for基本用法如下:

sum := 0
//第一种
for i:= 0; i < 10; i++ {
    sum += i
}
//第二种
for {
    sum++
    if sum > 100 {
        break
    }
}

for循环还可以配合range关键字来迭代数组,切片,通道以及集合map中的元素。如下:

nums := []int{2,3,4}
for i, num := range nums {
    fmt.Println(i, num)
}

在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对的 key 值。

for循环几点需要注意的地方:

  • 左花括号{必须和for处于同一行

  • Go语言中for循环同样支持多重赋值,但Go语言不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化多个变量

  • for循环同样支持continuebreak来控制循环,但break更高级,可以选择中断哪一个循环。例如:

    for i:=0; i < 5; i++ {
        for j:= 0; j < 10; j++ {
            if j > 5 {
                break ILoop //此处break终止的是ILoop标签处的外层循环
            }
        } 
    }
    ILoop:
      statement
    

跳转goto语句

goto语句表示跳转到本函数的某个标签。如:

func myfunc() {
    i := 0
    HERE:
    fmt.Println(i)
    i++
    if i < 10 {
        goto HERE
    }
}

函数

函数声明

函数一般按照如下格式声明:func name(parameter-list)(result-list) { body }parameter-list表明函数的参数以及类型,result-list描述函数返回值名字以及类型,函数的返回值不是必须的,但是如果一个函数声明包含返回值,则必须以return语句结尾,除非函数无法运行至结尾,例如函数中有无限循环等情况。下面的4中声明方法都是正确的:

func add(x int, y int) int {return x+y}
func sub(x, y int) (z int) {z = x - y; return}
func first(x int, _ int) int { return x}
func zero(int, int) int {return 0}
多返回值

在前面已经了解,Go中函数可以有多个返回值。如果命名了返回值参数,一个没有参数的return语句,会将当前的值作为返回值返回。但如果遇到if等代码块和返回值同名,则需要显式写出返回值。

func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return 
}
错误处理

Go语言定义了关于错误处理的标准模式,即error接口。

error的类型可能是nil或non-nil, nil意味着运行成功,non-nil表示失败。当函数调用返回错误时,一般常用的处理方式:

  1. 最常用的方式是将错误直接返回给调用方。如:

    resp, err := http.Get(url)
    if err != nil {
        return nil, err
    }
    
  2. 重新尝试失败的操作,但是注意限制重试的时间间隔或者重试次数

  3. 如果错误发生,程序无法继续运行,则需要输出错误信息并结束程序

  4. 如果错误不严重,可以直接忽略掉错误。

看一个例子,例子展示如何从标准输入中读取字符,以及判断文件结束。io包保证任何由文件结束引起的读取失败都返回同一个错误-io.EOF

in := bufio.NewReader(os.Stdin)

for {
    r, _, err := in.ReadRune()
    if err == io.EOF {
        break
    }
    if err != nil {
        return fmt.Errorf("read failed:%v", err)
    }
}
匿名函数和闭包

匿名函数顾名思义,就是不带函数名的函数,在Go语言中,可以随时在代码里定义匿名函数。例如:

func main() {
    var v func(a int) int
    
    v = func(a int) int {
        return a++
    }
}

函数可以像普通变量一样被传递或引用。

闭包和匿名函数不太一样,但是在Go中闭包必须由匿名函数实现。闭包中包含自由变量的代码块,自由变量指的是未绑定到特定对象的变量。同时闭包还包含自由变量当时的环境,即作用域。我们看一个例子:

func main() {
    var j int = 5
    
    a := func()(func()) {
        var i int = 10
        return func() {
            fmt.Println("i, j: %d, %d\n", i, j)
        }
    }()
    
    a()
    j *= 2
    a()
}

运行结果是:

i, j: 10, 5
i, j: 10, 10 

由于变量a指向的闭包函数引用了局部变量i和j, i的值被隔离,在闭包外不能被修改,改变j的值以后,再次调用a, 发现结果是修改的值。

需要注意的地方是:下面的代码是有问题的

var strs []func()
var strSlice = []string{"Hello", "Learn", "the", "Go", "Language"} 
for _, str := range strSlice {
    strs = append(strs, func() {
        fmt.Println(str)
    })
}

for _, str1 := range strs {
    str1()
}

问题原因在于循环变量的作用域。在第一个for循环中闭包记录的是循环变量str的内存地址,并不是每次循环变量str的值。因此在调用str1()的时候,strs里面其实保存的值已经是循环迭代完的值,即“Language”。

Go底层实现闭包的原理:其实是闭包返回的函数指针指向的内存区域是一个结构体,该结构体包含了匿名函数的指针和自由变量的指针。

解决办法就是每次复制变量str然后传到匿名函数中,让闭包的环境变量不相同。即:

for _, str := range strSlice {
    s := str
    strs = append(strs, func() {
        fmt.Println(s)
    })
}

关于闭包的解释,这里推荐一篇文章:「Closures in Go」,看完以后感觉对闭包会有更深的理解。

更多文章可以关注公众号:程序员Morgan.

欢迎扫码关注
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容