go语言学习——2.x

文章目录

  • 控制结构
    • if-else
    • switch
    • for(range)
      • break和continue
      • 标签与goto
  • 函数
    • 参数与返回值
      • 传递变长参数
      • defer
      • 内置函数
      • 递归函数
      • 函数作为参数
      • 闭包
      • 计算函数的执行时间

控制结构

if-else

if condition{
	//do something
}
if condition{
	//do something
}else{
	//do something
}
if condition1{
	//do something
}else if condition2{
	//do something
}else{
	//cathc-all or default
}
//else-if分支没有数量限制,但为了代码的可读性,不要在if后面加入太多的else-if结构。
//当if结构中有break、continue、goto或return语句时,常省略else部分
if condition{
	return x
}
return y
//不要同时在if-else结构的两个分支里都使用return语句,这会导致编译错误
//判断go程序的操作系统类型
if runtime.GOOS == "windows"{
	...
}else{//unix-like
	...
}

if可以包含一个初始化语句,比如给一个变量赋值,具体格式如下:

if initialization; condition{
	// do something
}
val:=10
if val>max{
	//...
}
//可以写成
//使用:=声明的变量作用域只存在于if结构中
if val:=10; val>max{
	//...
}


if value:=process(data); value>max{
	...
}

strconv.Atoi可以将一个字符串转换为一个整数

anInt, _ = strconv.Atoi(origStr)

如果不能被转换为整数,anInt的值会变成0,而_忽视了错误,程序会继续运行,正确的做法是检查错误

package main

import (
	"fmt"
	"strconv"
)

func main(){
	var orig string="ABC"
	var newS string
	
	fmt.Printf("The size of ints is: %d\n",strconv.IntSize)
	
	an,err:=strconv.Atoi(orig)
	if err!=nil{
		fmt.Printf("orig %s is not an integer - exiting with error\n",orig)
		return
	}
	fmt.Printf("The integer is %d\n",an)
	an=an+5
	newS=strconv.Itoa(an)
	fmt.Printf("The new string is :%s\n",newS)
}
//习惯用法
value,err:=pack1.function1(param1)
if err!=nil{
	fmt.Printf("An error occured in pack1.function1 with parameter %v", param1)
	return err
}

//如果想在错误发生的同时终止程序运行,可以使用os包的Exit函数:
if err!=nil{
	fmt.Printf("Program stopping with err %v",err)
	os.Exit(1)
}
//推出代码1可以使用外部脚本获取到

switch

switch var1 {
    case val1:
        ...
    case val2:
        ...
    default:
        ...
}
  • 每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止。
  • 一旦成功地匹配到某个分支,在执行完相应代码后就会退出整个 switch 代码块,不需要特别使用 break 语句来表示结束。
  • 程序也不会自动地去执行下一个分支的代码。如果在执行完每个分支的代码后,还希望继续执行后续分支的代码,可以使用 fallthrough 关键字来达到目的。
switch i {
    case 0: // 空分支,只有当 i == 0 时才会进入分支
    case 1:
        f() // 当 i == 0 时函数不会被调用
}

switch i {
    case 0: fallthrough
    case 1:
        f() // 当 i == 0 时函数也会被调用
}
switch {
    case condition1:
        ...
    case condition2:
        ...
    default:
        ...
}
switch initialization {
    case val1:
        ...
    case val2:
        ...
    default:
        ...
}

//示例
switch result := calculate(); {
    case result < 0:
        ...
    case result > 0:
        ...
    default:
        // 0
}

switch a, b := x[i], y[j]; {
    case a < b: t = -1
    case a == b: t = 0
    case a > b: t = 1
}

另外还有, select结构,用于channel选择,后续会提到

for(range)

for 初始化语句; 条件语句; 修饰语句 {}
package main

import "fmt"

func main() {
    for i := 0; i < 5; i++ {
        fmt.Printf("This is the %d iteration\n", i)
    }
}

同时使用多个计数器

for i, j := 0, N; i < j; i, j = i+1, j-1 {}

break和continue

一个 break 的作用范围为该语句出现后的最内部的结构,它可以被用于任何形式的 for 循环(计数器、条件判断等)。但在 switch 或 select 语句中,break 语句的作用结果是跳过整个代码块,执行后续的代码。

关键字 continue 忽略剩余的循环体而直接进入下一次循环的过程,但不是无条件执行下一次循环,执行之前依旧需要满足循环的判断条件。

标签与goto

for、switch 或 select 语句都可以配合标签(label)形式的标识符使用,即某一行第一个以冒号(:)结尾的单词

package main

import "fmt"

func main() {

LABEL1:
    for i := 0; i <= 5; i++ {
        for j := 0; j <= 5; j++ {
            if j == 4 {
                continue LABEL1
            }
            fmt.Printf("i is: %d, and j is: %d\n", i, j)
        }
    }

}
package main

func main() {
    i:=0
    HERE:
        print(i)
        i++
        if i==5 {
            return
        }
        goto HERE
}

函数

三种类型的函数:

  • 普通的带名字的函数
  • 匿名函数或者lambda函数
  • 方法(Method)

除了main()、init()函数外,其他所有类型的函数都可以有参数与返回值。函数参数、返回值以及它们的类型被统称为函数签名。

package main

func main(){
	greeting()
}

func greeting(){
	println("In greeting: Hi!!!")
}

参数与返回值

接收参数,返回零个或多个值

通过return关键字返回一组值。

有返回值的函数都必须以return或panic结尾

return之后的语句都不会被执行

函数定义时,形参一般是有名字的,也可以定义没有形参名的函数,只有相应的形参类型:func f(int, int, float64)

没有参数的函数通常被称为niladic函数,就像mian.main()

按值传递call by value、按引用传递call by reference

go默认使用值传递来传递参数,也就是传递参数的副本。对传递的值进行修改,不会影响原来的变量。Function(arg1)

如果希望函数可以直接修改参数的值,而不是对副本进行操作,需要将参数的地址传递给函数(变量名前加&符号),这就是按引用传递。Function(&arg1)。此时传递给函数的是一个指针,指针的值(一个地址)会被复制,但指针的值所指向的地址上的值不会被复制;通过这个指针的值来修改这个值所指向的地址上的值。

传递指针的消耗比传递副本来的少。

在函数调用时,切片、字典、接口、通道这样的引用类型都默认使用引用传递(即使没有显式的指出指针)

package main

import "fmt"

func main() {
    fmt.Printf("Multiply 2 * 5 * 6 = %d\n", MultiPly3Nums(2, 5, 6))
    // var i1 int = MultiPly3Nums(2, 5, 6)
    // fmt.Printf("MultiPly 2 * 5 * 6 = %d\n", i1)
}

func MultiPly3Nums(a int, b int, c int) int {
    // var product int = a * b * c
    // return product
    return a * b * c
}
//Multiply 2 * 5 * 6 = 60

命名的返回值
当需要返回多个非命名返回值时,需要使用 () 把它们括起来,比如 (int, int)

命名返回值作为结果形参(result parameters)被初始化为相应类型的零值,当需要返回的时候,我们只需要一条简单的不带参数的 return 语句。需要注意的是,即使只有一个命名返回值,也需要使用 () 括起来

package main

import "fmt"

var num int = 10
var numx2, numx3 int

func main() {
    numx2, numx3 = getX2AndX3(num)
    PrintValues()
    numx2, numx3 = getX2AndX3_2(num)
    PrintValues()
}

func PrintValues() {
    fmt.Printf("num = %d, 2x num = %d, 3x num = %d\n", num, numx2, numx3)
}

func getX2AndX3(input int) (int, int) {
    return 2 * input, 3 * input
}

func getX2AndX3_2(input int) (x2 int, x3 int) {
    x2 = 2 * input
    x3 = 3 * input
    // return x2, x3
    return
}

空白符

package main

import "fmt"

func main() {
    var i1 int
    var f1 float32
    i1, _, f1 = ThreeValues()
    fmt.Printf("The int: %d, the float: %f \n", i1, f1)
}

func ThreeValues() (int, int, float32) {
    return 5, 6, 7.5
}

改变外部变量

传递指针,赋予了函数直接修改外部变量的能力,所以被修改的变量不再需要使用return返回

package main

import (
    "fmt"
)

// this function changes reply:
func Multiply(a, b int, reply *int) {
    *reply = a * b
}

func main() {
    n := 0
    reply := &n
    Multiply(10, 5, reply)
    fmt.Println("Multiply:", *reply) // Multiply: 50
}

传递变长参数

如果函数的最后一个参数是采用...type的形式,函数就可以处理一个变长的参数,这个长度可以为0,这样的函数称为变长函数。

func myFunc(a,b,arg ....int){}
func Greeting(prefix string, who ...string)
Greeting("hello:","joe","anna","eileen")

如果参数被存储在一个slice类型的变量中,可以通过slice… 的形式来传递参数调用变参函数

package main

import "fmt"

func main() {
    x := min(1, 3, 2, 0)
    fmt.Printf("The minimum is: %d\n", x)
    slice := []int{7,9,3,5,1}
    x = min(slice...)
    fmt.Printf("The minimum in the slice is: %d", x)
}

func min(s ...int) int {
    if len(s)==0 {
        return 0
    }
    min := s[0]
    for _, v := range s {
        if v < min {
            min = v
        }
    }
    return min
}
//The minimum is: 0
//The minimum in the slice is: 1

defer

关键字defer允许推迟到函数返回之前(或者任意位置执行return语句之后)一刻才执行某个语句或函数

defer和return的执行顺序是先返回值赋值,然后执行defer,然后return到函数调用处

package main
import "fmt"

func main() {
    function1()
}

func function1() {
    fmt.Printf("In function1 at the top\n")
    defer function2()
    fmt.Printf("In function1 at the bottom!\n")
}

func function2() {
    fmt.Printf("function2: Deferred until the end of the calling function!")
}
//In Function1 at the top
//In Function1 at the bottom!
//Function2: Deferred until the end of the calling function!
func a() {
    i := 0
    defer fmt.Println(i)
    i++
    return
}
//打印0

有多个defer行为被注册时,会以逆序执行

func f() {
    for i := 0; i < 5; i++ {
        defer fmt.Printf("%d ", i)
    }
}
// 4 3 2 1 0

defer将语句放入栈中,也会将相关的值拷贝同时入栈。

func sum(n1 int, n2 int) int {
	defer fmt.Println("n1=",n1)
	defer fmt.Println("n2=",n2)
	n1++//11
	n2++//21
	res:=n1+n2
	fmt.Println("res=",res)
	return res
}

func main(){
	res:=sum(10,20)
	fmt.Println("main res=",res)
}
//res= 32
//n2= 20
//n1= 10
//main res= 32

defer允许进行一些函数执行完成后的收尾工作:
1、关闭文件流

defer file.Close()

2、解锁加密资源

mu.Lock()
defer mu.Unlock()

3、打印最终报告

printHeader()
defer printFooter()

4、关闭数据库连接

defer disconnectFromDB()

使用defer语句实现代码追踪

package main

import "fmt"

func trace(s string)   { fmt.Println("entering:", s) }
func untrace(s string) { fmt.Println("leaving:", s) }

func a() {
    trace("a")
    defer untrace("a")
    fmt.Println("in a")
}

func b() {
    trace("b")
    defer untrace("b")
    fmt.Println("in b")
    a()
}

func main() {
    b()
}
//entering: b
//in b
//entering: a
//in a
//leaving: a
//leaving: b
package main

import "fmt"

func trace(s string) string {
    fmt.Println("entering:", s)
    return s
}

func un(s string) {
    fmt.Println("leaving:", s)
}

func a() {
    defer un(trace("a"))
    fmt.Println("in a")
}

func b() {
    defer un(trace("b"))
    fmt.Println("in b")
    a()
}

func main() {
    b()
}

内置函数

go语言学习——2.x_第1张图片

递归函数

package main

import "fmt"

func main() {
    result := 0
    for i := 0; i <= 10; i++ {
        result = fibonacci(i)
        fmt.Printf("fibonacci(%d) is: %d\n", i, result)
    }
}

func fibonacci(n int) (res int) {
    if n <= 1 {
        res = 1
    } else {
        res = fibonacci(n-1) + fibonacci(n-2)
    }
    return
}

函数作为参数

函数可以作为其他函数进行传递,然后在其他函数内调用,一般称之为回调。

package main

import (
    "fmt"
)

func main() {
    callback(1, Add)
}

func Add(a, b int) {
    fmt.Printf("The sum of %d and %d is: %d\n", a, b, a+b)
}

func callback(y int, f func(int, int)) {
    f(y, 2) // this becomes Add(1, 2)
}
//The sum of 1 and 2 is: 3

闭包

当不希望给函数起名字的时候,可以使用匿名函数,func(x, y int) int{ return x+y }

这样的函数不能独立存在,但可以被赋予某个变量,即保存函数的地址到变量中:fplus:=func(x,y int) int {return x+y},然后通过变量名对函数进行调用:fplus(3,4)

也可以直接对匿名函数进行调用:func(x,y int) int {return x+y}(3,4)

func() {
    sum := 0
    for i := 1; i <= 1e6; i++ {
        sum += i
    }
}()
//花括号 {} 涵盖着函数体,最后的一对括号表示对该匿名函数的调用。

defer语句和匿名函数

关键字defer经常配合匿名函数使用,可以用于改变函数的命名返回值。

匿名函数同样被称之为闭包,允许调用定义在其他环境中的变量。

函数作为返回值

package main

import "fmt"

func main() {
    // make an Add2 function, give it a name p2, and call it:
    p2 := Add2()
    fmt.Printf("Call Add2 for 3 gives: %v\n", p2(3))
    // make a special Adder function, a gets value 2:
    TwoAdder := Adder(2)
    fmt.Printf("The result is: %v\n", TwoAdder(3))
}

func Add2() func(b int) int {
    return func(b int) int {
        return b + 2
    }
}

func Adder(a int) func(b int) int {
    return func(b int) int {
        return a + b
    }
}
//Call Add2 for 3 gives: 5
//The result is: 5
package main

import "fmt"

func main() {
    var f = Adder()
    fmt.Print(f(1), " - ")
    fmt.Print(f(20), " - ")
    fmt.Print(f(300))
}

func Adder() func(int) int {
    var x int
    return func(delta int) int {
        x += delta
        return x
    }
}
//1 - 21 - 321

在多次调用中,变量 x 的值是被保留的,即 0 + 1 = 1,然后 1 + 20 = 21,最后 21 + 300 = 321:闭包函数保存并积累其中的变量的值,不管外部函数退出与否,它都能够继续操作外部函数中的局部变量。

计算函数的执行时间

time包的Now()和Sub函数

start := time.Now()
longCalculation()
end := time.Now()
delta := end.Sub(start)
fmt.Printf("longCalculation took this amount of time: %s\n", delta)

你可能感兴趣的:(go,golang,学习)