学习Go语言必备案例 (2)

11. 函数

函数 是 Go 的中心。

package main

import (
	"fmt"
)
//这里是一个函数,接受两个 int 并且以 int 返回它们的和
func plus(a int, b int) int {
	//Go 需要明确的返回值,例如,它不会自动返回最后一个表达式的值
	return a + b
}

func main() {
	//通过 name(args) 来调用一个函数
	res := plus(1, 2)
	fmt.Println("1+2 =", res)
}

执行结果如下图所示:
在这里插入图片描述

这里有许多 Go 函数的其他特性。其中一个就是多值返回

12. 多返回值

Go 内建多返回值支持。这个特性在 Go 语言中经常被用到,例如用来同时返回一个函数的结果和错误信息。

package main

import (
	"fmt"
)
//(int, int) 在这个函数中标志着这个函数返回 2 个 int
func vals() (int, int) {
	return 3, 7
}

func main() {
	//通过多赋值 操作来使用这两个不同的返回值。
	a, b := vals()
	fmt.Println(a)
	fmt.Println(b)

	//如果想返回值的一部分的话,你可以使用空白定义符 _。
	_, c := vals()
	fmt.Println(c)
}

执行结果如下图所示:
在这里插入图片描述

13. 变参函数

可变参数函数。可以用任意数量的参数调用。例如,fmt.Println 是一个常见的变参函数。

package main

import (
	"fmt"
)
//这个函数使用任意数目 的 int 作为参数。
func sum(nums ...int) {
	fmt.Print(nums, " ")
	total := 0
	for _, num := range nums {
		total += num
	}
	fmt.Println(total)
}

func main() {
	//变参函数使用常规的调用方式,除了参数比较特殊
	sum(1, 2)
	sum(1, 2, 3)
	
	//如果你的 slice 已经有了多个值,想把它们作为变参使用,你要这样调用 func(slice...)。
	nums := []int{1, 2, 3, 4}
	sum(nums...)
}

执行结果如下图所示:
在这里插入图片描述

14. 闭包

Go 支持通过 闭包来使用 匿名函数。匿名函数在你想定义一个不需要命名的内联函数时是很实用的。

package main

import (
	"fmt"
)

//这个 intSeq 函数返回另一个在 intSeq 函数体内定义的匿名函数。
// 这个返回的函数使用闭包的方式 隐藏 变量 i
func intSeq() func() int {
	i := 0
	return func() int {
		i += 1
		return i
	}
}

func main() {
	//我们调用 intSeq 函数,将返回值(也是一个函数)赋给nextInt。
	// 这个函数的值包含了自己的值 i,这样在每次调用 nextInt 时都会更新 i 的值。
	nextInt := intSeq()

	//通过多次调用 nextInt 来看看闭包的效果。
	fmt.Println(nextInt())
	fmt.Println(nextInt())
	fmt.Println(nextInt())

	//为了确认这个状态对于这个特定的函数是唯一的,我们重新创建并测试一下。
	newInts := intSeq()
	fmt.Println(newInts())
}

执行结果如下图所示:
在这里插入图片描述

15. 递归

Go 支持 递归。这里是一个经典的阶乘示例。

package main

import (
	"fmt"
)
//face 函数在到达 face(0) 前一直调用自身。
func fact(n int) int {
	if n == 0 {
		return 1
	}
	return n * fact(n-1)
}

func main() {
	fmt.Println(fact(5))
}

执行结果如下图所示:
在这里插入图片描述

16. 指针

Go 支持 指针,允许在程序中通过引用 传递 值或者数据结构。

package main

import (
	"fmt"
)

//我们将通过两个函数:zeroval 和 zeroptr 来比较指针和值类型的不同。
// zeroval 有一个 int 型参数,所以使用值传递。
// zeroval 将从调用它的那个函数中得到一个 ival形参的拷贝。
func zeroval(ival int) {
	ival = 0
}

//zeroptr 有一和上面不同的 *int 参数,意味着它用了一个 int指针。
// 函数体内的 *iptr 接着解引用 这个指针,从它内存地址得到这个地址对应的当前值。
// 对一个解引用的指针赋值将会改变这个指针引用的真实地址的值。
func zeroptr(iptr *int) {
	*iptr = 0
}

func main() {
	i := 1
	fmt.Println("initial:", i)
	zeroval(i)
	fmt.Println("zeroval:", i)
	
	//通过 &i 语法来取得 i 的内存地址,例如一个变量i 的指针。
	zeroptr(&i)
	fmt.Println("zeroptr:", i)
	
	//指针也是可以被打印的。
	fmt.Println("pointer:", &i)
}

执行结果如下图所示:
在这里插入图片描述
zeroval 在 main 函数中不能改变 i 的值,但是zeroptr 可以,因为它有一个这个变量的内存地址的引用。

17. 结构体

Go 的结构体 是各个字段字段的类型的集合。这在组织数据时非常有用。

package main

import (
"fmt"
)
//这里的 person 结构体包含了 name 和 age 两个字段
type person struct {
	name string
	age  int
}

func main() {
	//使用这个语法创建了一个新的结构体元素
	fmt.Println(person{"Bob", 20})

	//你可以在初始化一个结构体元素时指定字段名字。
	fmt.Println(person{name: "Alice", age: 30})

	//省略的字段将被初始化为零值。
	fmt.Println(person{name: "Fred"})

	//& 前缀生成一个结构体指针。
	fmt.Println(&person{name: "Ann", age: 40})

	//使用点来访问结构体字段。
	s := person{name: "Sean", age: 50}
	fmt.Println(s.name)
	fmt.Println(s.age)

	//也可以对结构体指针使用. - 指针会被自动解引用。
	sp := &s
	fmt.Println(sp.age)

	//结构体是可变的。
	sp.age = 51
	fmt.Println(sp.age)
}

执行结果如下图所示:
在这里插入图片描述

18. 方法

Go 支持在结构体类型中定义方法 。

package main

import (
"fmt"
)

type rect struct {
	width, height int
}
//这里的 area 方法有一个接收器类型 rect。算矩形的面积
func (r *rect) area() int {
	return r.width * r.height
}
//可以为值类型或者指针类型的接收器定义方法。这里是一个值类型接收器的例子。
//计算矩形的周长
func (r rect) perim() int {
	return 2*r.width + 2*r.height
}

func main() {
	r := rect{width: 10, height: 5}
	//这里我们调用上面为结构体定义的两个方法。
	fmt.Println("area: ", r.area())
	fmt.Println("perim:", r.perim())
	
	//Go 自动处理方法调用时的值和指针之间的转化。
	//可以使用指针来调用方法来避免在方法调用时产生一个拷贝,或者让方法能够改变接受的数据。
	rp := &r
	fmt.Println("area: ", rp.area())
	fmt.Println("perim:", rp.perim())
}

执行结果如下图所示:
在这里插入图片描述

19. 接口

接口 : Go 语言中组织和命名相关的方法集合的机制。接口 是方法特征的命名集合。

package main

import (
	"fmt"
	"math"
)
//这里是一个几何体的基本接口
type geometry interface {
	area() float64
	perim() float64
}
//在我们的例子中,我们将让 rect 和 circle 实现这个接口
type rect struct {
	width, height float64
}
type circle struct {
	radius float64
}

//要在 Go 中实现一个接口,我们只需要实现接口中的所有方法。
// 这里我们让 rect 实现了 geometry 接口。
func (r rect) area() float64 {
	return r.width * r.height
}
func (r rect) perim() float64 {
	return 2*r.width + 2*r.height
}
//circle 的实现。
func (c circle) area() float64 {
	return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
	return 2 * math.Pi * c.radius
}
//如果一个变量的是接口类型,那么我们可以调用这个被命名的接口中的方法。
//这里有一个一通用的 measure 函数,利用这个特性,它可以用在任何 geometry(几何学) 上。
func measure(g geometry) {
	fmt.Println(g)
	fmt.Println(g.area())
	fmt.Println(g.perim())
}

func main() {
	r := rect{width: 3, height: 4}
	c := circle{radius: 5}

	//结构体类型 circle 和 rect 都实现了 geometry接口,
	// 所以我们可以使用它们的实例作为 measure 的参数。
	measure(r)
	measure(c)
}

执行结果如下图所示:
在这里插入图片描述

20. 错误处理

Go 语言使用一个独立的·明确的返回值来传递错误信息的。这与使用异常的 Java 和 Ruby 以及在 C 语言中经常见到的超重的单返回值/错误值相比,Go 语言的处理方式能清楚的知道哪个函数返回了错误,并能像调用那些没有出错的函数一样调用。

package main

import (
	"fmt"
	"errors"
)
//按照惯例,错误通常是最后一个返回值并且是 error 类型,一个内建的接口。
func f1(arg int) (int, error) {
	if arg == 42 {
		//errors.New 构造一个使用给定的错误信息的基本error 值。
		return -1, errors.New("can't work with 42")
	}
	//返回错误值为 nil 代表没有错误。
	return arg + 3, nil
}
//通过实现 Error 方法来自定义 error 类型是可以的。
// 这里使用自定义错误类型来表示上面的参数错误。
type argError struct {
	arg  int
	prob string
}
func (e *argError) Error() string {
	return fmt.Sprintf("%d - %s", e.arg, e.prob)
}
func f2(arg int) (int, error) {
	if arg == 42 {
		//在这个例子中,我们使用 &argError 语法来建立一个新的结构体,
		// 并提供了 arg 和 prob 这个两个字段的值。
		return -1, &argError{arg, "can't work with it"}
	}
	return arg + 3, nil
}
func main() {
	//下面的两个循环测试了各个返回错误的函数。
	// 注意在 if行内的错误检查代码,在 Go 中是一个普遍的用法。
	for _, i := range []int{7, 42} {
		if r, e := f1(i); e != nil {
			fmt.Println("f1 failed:", e)
		} else {
			fmt.Println("f1 worked:", r)
		}
	}
	for _, i := range []int{7, 42} {
		if r, e := f2(i); e != nil {
			fmt.Println("f2 failed:", e)
		} else {
			fmt.Println("f2 worked:", r)
		}
	}
	//你如果想在程序中使用一个自定义错误类型中的数据,
	// 你需要通过类型断言来得到这个错误类型的实例。
	_, e := f2(42)
	if ae, ok := e.(*argError); ok {
		fmt.Println(ae.arg)
		fmt.Println(ae.prob)
	}
}

执行结果如下图所示:
在这里插入图片描述

上一篇:学习Go语言必备案例 (1)

下一篇:学习Go语言必备案例 (3)

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据知道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值