《Go程序设计语言》读书笔记-函数

函数包含连续执行的语句,可以使用代码中通过调用函数来执行他们,函数能够将一个复杂的工作切分成多个更小的模块,使多人写作变得容易。另外,函数对他的使用者隐藏了实现细节。这几方面的特性使得函数成为多数编程语言的重要特性之一。

1. 函数声明

每个函数都包含一个名字,一个形参列表,一个可选的返回列表及函数体

func name(parameter-list) (result-list) {

  body

}

形参列表指定了函数返回值的类型,这些局部变量都由调用者提供的实参传递而来。返回值列表指定了函数返回值的类型。当函数返回一个未命名的返回值或没有返回值的时候,返回列表的圆括号可以省略。如果一个函数既省略返回列表页没有任何返回值,那么设计这个函数的目的是调用函数之后所带来的附加效果。在下面的hypot函数中:

func hypot(x, y float64) float64 {

  return math.Sqrt(x*x +y*y)

}

fmt.Println(hypot(3,4))    //"5"

x,y是函数声明中的形参,3,4是调用函数时的实参,并且返回了一个类型为float64的值。

返回值也可以像形参一样命名。这个时候每个命名的返回值会声明为一个局部变量,并且根据变量类型初始化为相应的0值。

当函数存在返回列表时,必须显示的以return语句结束,除非函数明确不会走完整个执行流程,比如在函数中抛出宕机异常或者函数体内存在一个没有break退出条件的无限for循环。

在hypot函数中使用到一种简写,如果几个形参或者返回值类型相同,那么类型只需要写一次。以下两个声明是完全相同的。

func f(i,j,k int, s,t string)

func f(i int, j int, k int, s string, t string)

下面使用4种方式声明一个带有两个形参和一个返回值的函数,所有变量都是int类型。空白标识符用来强调这个形参在函数中未使用。

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}

fmt.Printf(" %T\n",add)  //"func(int,int) int" 

fmt.Printf(" %T\n",sub)  //"func(int,int) int" 

fmt.Printf(" %T\n",first)  //"func(int,int) int" 

fmt.Printf(" %T\n",zero)  //"func(int,int) int" 

函数的类型称作函数签名。当两个函数拥有相同的形参列表和返回列表时,认为这两个函数的类型或签名是相同的。而形参和返回值的名字不会影响到函数类型,采用简写也不会影响函数的类型

每一次调用函数都需要提供实参类似对应每一个形参,包括参数的调用顺序也必须一致。Go语言没有默认参数值的概念也不能指定实参名,除了用于文档说明之外,形参和返回值的命名不会对调用方有任何影响。

形参变量都是函数的局部变量,初始值由调用者提供的实参传递。函数形参以及命名返回值同属于函数最外层作用域的局部变量。

实参是按值传递的,所以函数接收到的是每个实参的副本;修改函数的形参变量不会影响到调用者提供的实参。然鹅,如果提供的实参包含引用类型,比如指针,slice,map。函数或者通道,那么函数使用形参变量时就可能会间接的修改实参变量。

2 递归

函数可以递归调用,这意味着函数可以直接或间接地调用自己。递归是一种实用技术,可以处理许多带有递归特性的数据结构。

下面的代码示例使用了一个非标准的包golang.org/x/net/html,它提供解析HTML的功能。golang.org/x/...下的仓库(比如网络,国际化语言处理,移动平台,图片处理,加密功能以及开发者工具)都由Go团队负责设计和维护。这些包并不属于标准库,原因他们还正在开发中或者程序员很少使用。

我们需要的golang.org/x/net/html API如下面代码所示。函数html.parse读入一段字节序列,解析它们。然后返回HTML文档树的根节点html.node. HTML有多种节点,比如文本,注释等。但这里我们只关心表单的元素节点<name key='value'>.

主函数从标准输入中读入HTML,使用递归的visit函数获取HTML文本的超链接,并且把所有的超链接输出。

func main() {
	doc, err := html.Parse(os.Stdin)
	if err != nil {
		fmt.Fprintf(os.Stderr, "findlinks1: %v\n", err)
		os.Exit(1)
	}
		for _, link := range visit(nil,doc) {
			fmt.Println(link)
		}

}

  

visit函数遍历HTML树上的所有节点,从HTML锚元素<a href='...'>中得到href属性的内容,将获取到的链接内容添加到字符串slice,最后返回这个slice:

//visit函数会将n节点中每个链接添加到结果中。
func visit(links []string,n *html.Node)[]string	{
	if n.Type == html.ElementNode && n.Data == "a" {
		for _, a := range n.Attr {
			if a.Key == "href" {
				links = append(links,a.Val)
			}
		}
	}
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		links = visit(links,c)

	}
	return links
}

  

要对树中的任意节点n进行递归,visit递归的调用自己去访问节点n的所有自子节点并且将访问过的节点保存在firstchild链表中。我们在Go主页运行findlinks,使用管道将以前的fetch程序的输出去定向到findlinks。

allanyang-mbp:src allanyang$ go build gopl.io/ch1/fetch
allanyang-mbp:src allanyang$ go build gopl.io/ch5/findlinks1
allanyang-mbp:src allanyang$ ./fetch https://golang.org | ./findlinks1
/
/
#
/doc/
/pkg/
/project/
/help/
/blog/
#
#
//tour.golang.org/
/dl/
//blog.golang.org/
https://developers.google.com/site-policies#restrictions
https://creativecommons.org/licenses/by/3.0/
/LICENSE
/doc/tos.html
http://www.google.com/intl/en/policies/privacy/

可以注意到会获取各种不同形式的超链接,之后我们将看到如何解析这些地址,并将链接都转换为基于https://golang.org的URL绝对路径

猜你喜欢

转载自www.cnblogs.com/nurruden/p/10682912.html