Go学习笔记二(函数)

函数

函数作用域

在 Go 中,定义在函数外的变量是全局的,那些定义在函数内部的变量,对于函数来说是局部的。
局部 变量 仅仅 在执行定义它的函数时有效。

package main
import "fmt"

var a int
func main() {
    a = 5
    println(a)
    f()
}
func f() {
    a := 6
    println(a)
    g()
}
func g() {
    println(a)
}
//565

延迟代码(defer )

//在这里有许多重复的代码。为了解决这些,Go 有了 defer 语句。在 defer 后指定的函数会在函数退出 前 调用。
func ReadWrite() bool {
    file.Open("file")
    // 做一些工作
    if failureX {
        file.Close() //←重复代码
        return false
    }
    if failureY {
        file.Close() //←重复代码
        return false
    }
    file.Close() //←重复代码
    return true
}
//====================
//用defer改进
func ReadWrite() bool {
    file.Open("file")
    defer file.Close() //← file.Close() 被添加到了 defer 列表
    // 做一些工作
    if failureX {
        return false //← Close() 现在自动调用
    }
    if failureY {
        return false //← 这里也是
    }
    return true// ← And here
}

可以将多个函数放入 “延迟列表”中

package main
import "fmt"

var a int
func main() {
    for i := 0 ; i < 5 ; i++ {

        //fmt.Printf("%d ", i)//0 1 2 3 4
        //延迟的函数是按照后进先出(LIFO)的顺序执行
        defer fmt.Printf("%d ", i)//4 3 2 1 0
    }   
}

利用 defer 甚至可以修改返回值

package main
// import "fmt"

var a int
func main() {
    println(f())
}

//在这个(匿名)函数中,可以访问任何命名返回参数:
//在 defer 中访问返回值
func f() (ret int ) { //← ret 初始化为零
    defer func () {
        ret++ //← ret 增加为 1
    } ()
    return 0 //← 返回的是 1 而 不是 0 !
}
defer func () {
/* ... */
} () // ← () 在这里是必须的


//或者这个例子,更加容易了解为什么,以及在哪里需要括号:
//带参数的函数符号
defer func (x int ) {
/* ... */
} (5) //← 为输入参数 x 赋值 5

变参

接受不定数量的参数的函数叫做变参函数。定义函数使其接受变参:
arg ... int 告诉 Go 这个函数接受不定数量的参数。注意,这些参数的类型全部是 int 。
如果不指定变参的类型,默认是空的接口 interface{}

func myfunc(arg ... int ) {}

恐慌(Panic)和恢复(Recover)

Go 没有像 Java 那样的异常机制,例如你无法像在 Java 中那样抛出一个异常。作为替代,它使用了恐慌和恢复(panic-and-recover)机制。一定要记得,这应当作为最后的手段被使用,你的代码中应当没有,或者很少的令人恐慌的东西。这是个强大的工具,明智的使用它。那么,应该如何使用它呢。
Panic
是一个内建函数,可以中断原有的控制流程,进入一个令人恐慌的流程中。当函数 F 调用 panic ,函数 F 的执行被中断,并且 F 中的延迟函数会正常执行,然后 F 返回到调用它的地方。在调用的地方, F 的行为就像调用了 panic 。这一过程继续向上,直到程序崩溃时的所有 goroutine 返回。
恐慌可以直接调用 panic 产生。也可以由 运行时错误 产生,例如访问越界的数组。
Recover
是一个内建的函数,可以让进入令人恐慌的流程中的goroutine恢复过来。recover仅在延迟 函数中有效。
在正常的执行过程中,调用 recover 会返回 nil 并且没有其他任何效果。如果
当前的 goroutine 陷入恐慌,调用 recover 可以捕获到 panic 的输入值,并且恢复正常的执行。

函数练习

// Q5. (0) 平均值
// 1. 编写一个函数用于计算一个 float64 类型的 slice 的平均值。
package main 
import "fmt"
func main() {
    s := [] float64{1.25,0.98,3.96}
    fmt.Printf("%v\n",average(s))
}

func average(xs []float64) (avg float64) {
    sum := 0.0
    switch len(xs){
    case 0:
        avg = 0 //如果长度是零,返回 0;
    default :
        for _, v := range xs {
            sum += v
        }        
        avg = sum /float64(len(xs)) //为了使除法能正常计算,必须将值转换为 float64 ;
    }
    return  //得到平均值,返回它
}
// Q6. (0) 整数顺序
// 1. 编写函数,返回其(两个)参数正确的(自然)数字顺序:
// f(7,2) → 2,7
// f(2,7) → 2,7
package main 
import "fmt"
func main() {
    s := [] float64{1.25,0.98,3.96}
    fmt.Printf("%v\n",average(s))
}

func order(a,b int) (int , int) {
    if a > b
        return b,a
    return a,b    
}
// 1. 创建一个固定大小保存整数的栈。它无须超出限制的增长。定义 push 函数——
// 将数据放入栈,和 pop 函数——从栈中取得内容。栈应当是先进后出,后进先出(LIFO)
// 的。
package main

import "fmt"

type stack struct { //← 栈 不应该被导出
    i int   //指向最后一个元素的索引
    data [10] int
}
//向函数 push 提供一个指向栈的指针
//这里不用指针不会有真正的结果,因为func (s stack) push(k int ) push 函数得到的是s的副本

func (s *stack) push(k int ) { //← 工作于参数的副本
    if s.i+1 > 9 {
        return
    }
    // println(k)
    // println(s.i)
    s.data[s.i] = k
    s.i++
}
func (s *stack) pop() int {
    s.i--
    return s.data[s.i]
}
func main() {
    var s stack //← 让 s 是一个 stack 变量
    s.push(25)
    fmt.Printf("stack %v\n", s) ;
    s.push(14)
    fmt.Printf("stack %v\n", s) ;
}
// 更进一步。编写一个 String 方法将栈转化为字符串形式的表达。可以这样的
// 方式打印整个栈: fmt.Printf("My stack %v\n", stack)
// 栈可以被输出成这样的形式: [0:m] [1:l] [2:k]
package main

import (
    "fmt"
    "strconv"
)

type stack struct { //← 栈 不应该被导出
    i int   //指向最后一个元素的索引
    data [10] int
}
func (s *stack) push(k int ) { //← 工作于参数的副本
    if s.i+1 > 9 {
        return
    }
    // println(k)
    // println(s.i)
    s.data[s.i] = k
    s.i++
}
func (s stack) String() string {
    var str string
    for i := 0 ; i <= s.i ; i++ {
        str = str + "[" +
        strconv.Itoa(i) + ":" + strconv.Itoa(s.data[i]) + "]"
    }
    return str
}
func main() {
    var s stack
     s.push(25)
     s.push(14)
     s.push(88)
    fmt.Printf("%v\n",s)
    fmt.Printf("%v\n",s.String())
}


// Q9. (1) 变参
// 1. 编写函数接受整数类型变参,并且每行打印一个数字。
package main 

import "fmt"

func main() {
    printthem(1,4,5,7,8)
    //printthem(1,6,9)
}

func printthem(numbers ... int ) {//← numbers 现在是整数类型的 slice
    for _,d := range numbers{
        fmt.Printf("%d\n",d)
    }
}
// Q10. (1) 斐波那契
// 1. 斐波那契数列以:1,1,2,3,5,8,13,... 开始。或者用数学形式表达:x 1 = 1;x 2 =
// 1;x n = x n−1 + x n−2 ∀n > 2。
// 编写一个接受 int 值的函数,并给出这个值得到的斐波那契数列。

package main 

// import "fmt"

func fibonacci( value int ) []int {
    x := make([]int , value)//创建一个用于保存函数执行结果的 array ;
    x[0],x[1] = 1 ,1
    for n := 2; n < value ; n++{
        x[n] = x[n-1] + x[n-2]
    }
    return x //返回 整个 array;
}
func main() {
    //使用关键字 range 可以 “遍历” 数字得到斐波那契函数返回的序列。这有 10 个,且打印了出来
    for _, term := range fibonacci(10){
        println(term)
        //fmt.Printf("%v,",term)
    }
}
// map() 函数是一个接受一个函数和一个列表作为参数的函数。函
// 数应用于列表中的每个元素,而一个新的包含有计算结果的列表被返回。因此:
// map(f(),(a 1 ,a 2 ,...,a n−1 ,a n )) = (f(a 1 ),f(a 2 ),...,f(a n−1 ),f(a n ))
// 1. 编写 Go 中的简单的 map() 函数。它能工作于操作整数的函数就可以了。
// 2. 扩展代码使其工作于字符串列表
package main
import "fmt"
func main() {
    m := []int {1,2,3}
    f := func (i int) int {
        return i*i
    }
    fmt.Printf("%v",Map(f,m))
}

func Map(f func(int) int,l []int) []int {
    j := make([]int,len(l))
    for k,v:=range l{
        j[k] = f(v)
    }
    return j
}
// Q12. (0) 最小值和最大值
// 1. 编写一个函数,找到 int slice ( []int ) 中的最大值。
// 2. 编写一个函数,找到 int slice ( []int ) 中的最小值。
package main 

func main() {
    l := []int{0,2,6,9,10}
    println(max(l))

    println(min(l))
}
func max(l []int) (max int) {//使用了命名返回参数;
    max = l[0]
    for _, v:=range l{//对 l 循环。元素的序号不重要;

        if v > max {
            max = v // 如果找到了新的最大值,记住它;
        }
    }
    return  //当前的 max 值被返回
}

func min(l []int)(min int) {
    min = l[0]
    for _,v := range l{
        if v < min {
            min = v
        }
    }
    return
}
// Q13. (1) 冒泡排序
// 1. 编写一个针对 int 类型的 slice 冒泡排序的函数。
// 它在一个列表上重复步骤来排序,比较每个相邻的元素,并且顺序错
// 误的时候,交换它们。一遍一遍扫描列表,直到没有交换为止,这意
// 味着列表排序完成。算法得名于更小的元素就像 “ 泡泡 ” 一样冒到列表
// 的顶端。

// 1. 冒泡排序并不是最有效率的,对于 n 个元素它的算法复杂度是 O(n 2 )。快速排
// 序  是更好的排序算法。
// 但是冒泡排序容易实现。

package main 

import "fmt"

func main() {
    n := []int {5,-1,12,0,3,6} //定义一个引用类型slice
    fmt.Printf("unsorted %v\n",n)

    bubbleSort(n)
    fmt.Printf("sorted %v\n",n)
}
//由于 slice 是一个引用类型, bubblesort 函数可以工作,并且无须返回排序后的 slice。
func bubbleSort(n []int) {
    for i:=0;i < len(n) - 1; i++{
        for j:= i+1; j<len(n);j++{
            if n[j] < n[i] {
                n[i],n[j] = n[j],n[i]
            }
        }
    }
}
// Q14. (1) 函数返回一个函数
// 1. 编写一个函数返回另一个函数,返回的函数的作用是对一个整数 +2。函数的名
// 称叫做 plusTwo 。然后可以像下面这样使用:
// p := plusTwo()
// fmt.Printf("%v\n", p(2))
// 应该打印 4。
// 2. 使 1 中的函数更加通用化,创建一个 plusX(x) 函数,返回一个函数用于对整
// 数加上 x 。
package main 

import "fmt"

func main() {
    p2 := plusTwo()
    fmt.Printf("%v\n",p2(2))
    p3 := plusX(3)
    fmt.Printf("%v\n",p3(5))
}
func plusTwo() func(int) int {//定义新的函数返回一个函数
    return func(x int) int{ return x + 2} //函数符号,在返回语句中定义了一个 +2 的 函数
}
//这里使用了闭包
func plusX(x int ) func( int ) int {

    return func (y int ) int { return x + y }

}

函数闭包

参考文章:
http://www.jianshu.com/p/fa21e6fada70

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值