• golang函数


    1.函数声明

    函数声明包括函数名、形式参数列表、返回值列表(可省略)以及函数体。

    func function-name(param...) (result...) {
        body
    }
    

    形式参数列表描述了函数的参数名以及参数类型。这些参数作为局部变量,其值由参数调用者提供。返回值列表描述了函数返回值的变量名以及类型。如果函数返回一个无名变量或者没有返回值,返回值列表的括号是可以省略的。如果一个函数声明不包括返回值列表,那么函数体执行完毕后,不会返回任何值。

    func hypot(x, y float64) float64 {
        return math.Sqrt(x*x + y*y)
    }
    fmt.Println(hypot(3,4)) // "5"
    

    2.递归

    函数可以是递归的,这意味着函数可以直接或间接的调用自身。对许多问题而言,递归是一种强有力的技术,例如处理递归的数据结构。

    func a(i int) (res int){
        if i == 1 {
            return i 
        }
        return i * a(i - 1)
    }
    
    fmt.Println(a(5)) // 120
    

    3.多返回值

    在Go中,一个函数可以返回多个值。

    func calculation(a,b int)(add,sub int) {
    	add = a + b
        sub = a - b
    }
    

    4.错误

    在Go中有一部分函数总是能成功的运行,对各种可能的输入都做了良好的处理,使得运行时几乎不会失败,除非遇到灾难性的、不可预料的情况,比如运行时的内存溢出。导致这种错误的原因很复杂,难以处理,从错误中恢复的可能性也很低。

    panic是来自被调函数的信号,表示发生了某个已知的bug。一个良好的程序永远不应该发生panic异常。

    value, ok := cache.Lookup(key)
    if !ok {
        // ...cache[key] does not exist…
    }
    

    4.1. 错误处理策略

    当一次函数调用返回错误时,调用者有应该选择何时的方式处理错误。根据情况的不同,有很多处理方式.

    resp,err := http.Get("https://www.google.com")
    if err != nil {
        fmt.Println(err)
    }
    

    4.2. 文件结尾错误(EOF)

    函数经常会返回多种错误,这对终端用户来说可能会很有趣,但对程序而言,这使得情况变得复杂。很多时候,程序必须根据错误类型,作出不同的响应。让我们考虑这样一个例子:从文件中读取n个字节。如果n等于文件的长度,读取过程的任何错误都表示失败。如果n小于文件的长度,调用者会重复的读取固定大小的数据直到文件结束。这会导致调用者必须分别处理由文件结束引起的各种错误。基于这样的原因,io包保证任何由文件结束引起的读取失败都返回同一个错误——io.EOF,该错误在io包中定义:

    package io
    
    import "errors"
    
    // EOF is the error returned by Read when no more input is available.
    var EOF = errors.New("EOF")
    

    调用者只需通过简单的比较,就可以检测出这个错误。下面的例子展示了如何从标准输入中读取字符,以及判断文件结束。

    in := bufio.NewReader(os.Stdin)
    for {
        r, _, err := in.ReadRune()
        if err == io.EOF {
            break // finished reading
        }
        if err != nil {
            return fmt.Errorf("read failed:%v", err)
        }
        // ...use r…
    }
    

    因为文件结束这种错误不需要更多的描述,所以io.EOF有固定的错误信息——“EOF”。对于其他错误,我们可能需要在错误信息中描述错误的类型和数量,这使得我们不能像io.EOF一样采用固定的错误信息。在7.11节中,我们会提出更系统的方法区分某些固定的错误值。

    5.函数值

    在Go中,函数被看作第一类值(first-class values):函数像其他值一样,拥有类型,可以被赋值给其他变量,传递给函数,从函数返回。对函数值(function value)的调用类似函数调用。

    func add(a,b int) (sum int) {
        sum = a + b 
    }
    
    func main() {
        f = add
        fmt.Println(sum(1,2))
    }
    

    函数类型的零值是nil。调用值为nil的函数值会引起panic错误:

        var f func(int) int
        f(3) // 此处f的值为nil, 会引起panic错误
    

    6.匿名函数

    拥有函数名的函数只能在包级语法块中被声明,通过函数字面量(function literal),我们可绕过这一限制,在任何表达式中表示一个函数值。函数字面量的语法和函数声明相似,区别在于func关键字后没有函数名。函数值字面量是一种表达式,它的值被成为匿名函数(anonymous function)。

    func squares() func() int {
        var x int
        return func() int {
            x++
            return x * x
        }
    }
    func main() {
        f := squares()
        fmt.Println(f()) // "1"
        fmt.Println(f()) // "4"
        fmt.Println(f()) // "9"
        fmt.Println(f()) // "16"
    }
    

    7.可变参数

    参数数量可变的函数称为为可变参数函数。典型的例子就是fmt.Printf和类似函数。Printf首先接收一个的必备参数,之后接收任意个数的后续参数。

    在声明可变参数函数时,需要在参数列表的最后一个参数类型之前加上省略符号“...”,这表示该函数会接收任意数量的该类型参数。

    func main() {
    	fmt.Println(sum(1,2,3,4,5)) // 15
    	var sli = []int{1,2,3,4,5}
    	fmt.Println(sli)		// [1 2 3 4 5]
    	fmt.Println(sum(sli...)) // 15
    }
    func sum(values ...int) int {
    	sum := 0
    	for _, v := range values {
    		sum += v
    	}
    	return sum
    }
    

    8.Panic异常

    Go的类型系统会在编译时捕获很多错误,但有些错误只能在运行时检查,如数组访问越界、空指针引用等。这些运行时错误会引起painc异常。

    一般而言,当panic异常发生时,程序会中断运行,并执行此goroute上的defer函数。

    当某些不应该发生的场景发生时,我们就应该调用panic。

    	name := "zhaohaiyu"
    	switch name {
    	case "zhy":
    		fmt.Println("zhy")
    	case "haiyuzhao":
    		fmt.Println("haiyuzhao")
    	default:
    		panic("没有这个名字")
    	}
    

    虽然Go的panic机制类似于其他语言的异常,但panic的适用场景有一些不同。由于panic会引起程序的崩溃,因此panic一般用于严重错误,如程序内部的逻辑不一致。

    9.Recover捕获异常

    通常来说,不应该对panic异常做任何处理,但有时,也许我们可以从异常中恢复,至少我们可以在程序崩溃前,做一些操作。

    如果在deferred函数中调用了内置函数recover,并且定义该defer语句的函数发生了panic异常,recover会使程序从panic中恢复,并返回panic value。导致panic异常的函数不会继续运行,但能正常返回。在未发生panic时调用recover,recover会返回nil。

    让我们以语言解析器为例,说明recover的使用场景。考虑到语言解析器的复杂性,即使某个语言解析器目前工作正常,也无法肯定它没有漏洞。因此,当某个异常出现时,我们不会选择让解析器崩溃,而是会将panic异常当作普通的解析错误,并附加额外信息提醒用户报告此错误。

    	defer func ()  {
    		if p := recover();  p != nil {
    			fmt.Println(p)  // 主动抛错
                	// 可以进行写日志等操作
    		}
    	}()
    
    	panic("主动抛错")
    
  • 相关阅读:
    linux下的crontab定时执行任务命令详解
    494. Target Sum
    347. Top K Frequent Elements
    5. Longest Palindromic Substring
    时间复杂度计算方法
    62. Unique Paths
    54. Spiral Matrix && 59. Spiral Matrix II
    57. Insert Interval
    53. Maximum Subarray
    42.Trapping rain water
  • 原文地址:https://www.cnblogs.com/zhaohaiyu/p/13541229.html
Copyright © 2020-2023  润新知