Golang Go语言 学习笔记(4)
Golang Go语言 学习笔记(4)
函数是一组逻辑的集合,能把大的任务拆成一个一个小的任务,供软件的各个位置去调用。
函数声明
func name(parameter-list) (result-list) {
body
}
- 参数列表:参数类型相同可以只在最后一个相同参数后面定义参数类型
- 形参与实参:按顺序赋值形参,只有当实参是引用类型,如指针,slice(切片)、map、function、channel 等类型当做引用传递可以在函数内部修改实参的值,否则其他情况,形参只是实参的拷贝。
- 返回值:返回值可以没有或者多个返回值,只有一个返回值时,可以只写一个返回值类型即可
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”
递归
- 递归:函数自己调用自己就是递归调用。
- 大部分编程语言使用固定大小的函数调用栈,常见的大小从 64KB 到 2MB 不等。固定大小栈会限制递归的深度,当你用递归处理大量数据时,需要避免栈溢出;除此之外,还会导致安全性问题。与相反,Go 语言使用可变栈,栈的大小按需增加(初始时很小)。这使得我们使用递归时不必考虑溢出和安全问题。
多指返回函数
- 惯例前面返回期望的值,最后一个返回 erro 值
- 不需要处理的返回值,用_来处理
- 如果在返回值列表里定义好了返回值的名字,可以用默认 return。可以减少代码量,但是增加维护难度。不宜过度使用。
// CountWordsAndImages does an HTTP GET request for the HTML
// document url and returns the number of words and images in it.
func CountWordsAndImages(url string) (words, images int, err error) {
resp, err := http.Get(url)
if err != nil {
return
}
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
err = fmt.Errorf("parsing HTML: %s", err)
return
}
words, images = countWordsAndImages(doc)
return
}
func countWordsAndImages(n *html.Node) (words, images int) { /* ... */ }
错误
- go 不将返回的 erro 类型当初异常来处理,值当做预期的值来看待。
- go 错误处理策略
- 第一种传播的方式
- 第二种重试的方式:注意设置重试时间和次数
- 第三种输出错误并退出:这种策略只应在 main 中执行。对库函数而言,应仅向上传播错误,除非该错误意味着程序内部包含不一致性,即遇到了 bug,才能在库函数中结束程序
- 第四种有时,我们只需要输出错误信息就足够了,不需要中断程序的运行。我们可以通过 log 包提供函数
- 第五种,也是最后一种策略:我们可以直接忽略掉错误。
- 固定类型错误:io.EOF,可以根据错误信息执行特别的操作
函数值
- 在 Go 语言中函数被定义为第一类值,有类型,可以被赋值给其他变量,传递个给函数。
//利用函数变量便利 html 标签
// forEachNode 针对每个结点 x,都会调用 pre(x)和 post(x)。// pre 和 post 都是可选的。// 遍历孩子结点之前,pre 被调用 // 遍历孩子结点之后,post 被调用 func forEachNode(n *html.Node, pre, post func(n *html.Node)) {
if pre != nil {
pre(n)
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
forEachNode(c, pre, post)
}
if post != nil {
post(n)
}
}
var depth intfunc startElement(n *html.Node) {
if n.Type == html.ElementNode {
fmt.Printf("%s<%s>\n", depth2, “”, n.Data)
depth++
}
}
func endElement(n *html.Node) {
if n.Type == html.ElementNode {
depth–
fmt.Printf("%s</%s>\n", depth2, “”, n.Data)
}
}
匿名函数
- 通过这种方式定义的函数可以访问完整的词法环境( lexical environment ),这意味着在函数中定义的内部函数可以 引用 该函数的变量
// squares 返回一个匿名函数。// 该匿名函数每次被调用时都会返回下一个数的平方。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"
}
- 利用匿名函数实现拓扑排序算法
// prereqs 记录了每个课程的前置课程 var prereqs = map[string][]string{
"algorithms": {"data structures"},
"calculus": {"linear algebra"},
"compilers": {
"data structures",
"formal languages",
"computer organization",
},
"data structures": {"discrete math"},
"databases": {"data structures"},
"discrete math": {"intro to programming"},
"formal languages": {"discrete math"},
"networks": {"operating systems"},
"operating systems": {"data structures", "computer organization"},
"programming languages": {"data structures", "computer organization"},
}
func main() {
for i, course := range topoSort(prereqs) {
fmt.Printf("%d:\t%s\n", i+1, course)
}
}
func topoSort(m map[string][]string) []string {
var order []string
seen := make(map[string]bool)
var visitAll func(items []string)
visitAll = func(items []string) {
for _, item := range items {
if !seen[item] {
seen[item] = true
visitAll(m[item])
order = append(order, item)
}
}
}
var keys []string
for key := range m {
keys = append(keys, key)
}
sort.Strings(keys)
visitAll(keys)
return order
}
- 利用匿名函数实现广度搜索
// breadthFirst calls f for each item in the worklist.
// Any items returned by f are added to the worklist.
// f is called at most once for each item.func
breadthFirst(f func(item string) []string, worklist []string) {
seen := make(map[string]bool)
for len(worklist) > 0 {
items := worklist
worklist = nil
for _, item := range items {
if !seen[item] {
seen[item] = true
worklist = append(worklist, f(item)...)
}
}
}
}
func crawl(url string) []string {
fmt.Println(url)
list, err := links.Extract(url)
if err != nil {
log.Print(err)
}
return list
}
func main() {
// Crawl the web breadth-first,
// starting from the command-line arguments.
breadthFirst(crawl, os.Args[1:])
}
- for 的循环词法块中局部变量是引用的
可变参数函数
- 用...来做变量名,一般可变参数函数用来处理字符串
func errorf(linenum int, format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, "Line %d: ", linenum)
fmt.Fprintf(os.Stderr, format, args...)
fmt.Fprintln(os.Stderr)
}
linenum, name := 12, "count"
errorf(linenum, "undefined: %s", name) // "Line 12: undefined: count"
Deferred 函数
- defer 语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、加锁、释放锁。通过 defer 机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放。释放资源的 defer 应该直接跟在请求资源的语句后。
- defer 的声明是从前往后,触发是从后往前的触发
- 可以做调试函数
func bigSlowOperation() {
defer trace("bigSlowOperation")() // don't forget the
extra parentheses
// ...lots of work …
time.Sleep(10 * time.Second) // simulate slow
operation by sleeping
}
func trace(msg string) func() {
start := time.Now()
log.Printf("enter %s", msg)
return func() {
log.Printf("exit %s (%s)", msg,time.Since(start))
}
}
Panic 异常
- 类似 php throw 如果不捕获也会直接中断程序
- 一般只有严重错误,影响到程序的正常执行才会用到 panic
Recover 捕获异常
- 无法捕获的情况
- 沒加 recover 的 goroutine 里 panic
- os.Exit
- map 中特殊情况锁机制
总结
函数是程序逻辑的基本单位,是整个程序的基石,了解它的结构是,帮助我们写出合理优雅的代码的基础。总体来说 Go 的匿名函数给我比较深的印象,没想到用法可以大大减少代码量。
更多关于Golang Go语言 学习笔记(4)的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang Go语言 学习笔记(4)的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
当然,以下是对“Golang Go语言 学习笔记(4)”这一帖子的专业回复:
看到你在持续记录Go语言的学习笔记,这确实是一个非常好的学习习惯。对于Go语言(Golang)的深入学习,我有几点建议和心得可以与你分享:
-
并发编程:Go语言以其强大的并发处理能力著称。在这一阶段的学习中,务必掌握goroutines和channels的使用,它们是Go并发编程的基石。通过实践一些简单的并发案例,如并发下载、生产者-消费者模型等,可以加深理解。
-
接口与类型:Go语言的接口设计非常简洁且强大。学习如何定义接口、实现接口以及接口多态性的使用,对于编写灵活、可扩展的代码至关重要。
-
错误处理:Go语言鼓励显式地处理错误,这有助于编写健壮的程序。了解error类型、defer语句以及panic和recover机制,能够帮助你更好地处理程序中的异常情况。
-
标准库与第三方库:Go语言的标准库已经非常完善,涵盖了文件操作、网络编程、加密解密等多个方面。同时,也有很多优秀的第三方库可供选择。熟悉这些库的使用,可以大大提高开发效率。
-
实践项目:理论学习固然重要,但将所学知识应用到实际项目中才能真正巩固。可以尝试编写一些小型项目,如Web服务、命令行工具等,来检验自己的学习成果。
希望这些建议能对你的学习有所帮助。加油,期待你未来在Go语言领域的更多精彩分享!