上一篇 Go圣经-学习笔记之复合类型(三)
下一篇 Go圣经-学习笔记之函数值(二)
函数声明
下面给出函数的四种声明方法:
func add(x int, y int) int { return x+y } func add(x, y int) (z int) { z = x + y ; return z } func first(x int, _ int) int { return x } func second(int, int) int { return 0 }
存在必有其存在的价值,那后两者存在的应用场景在哪里呢?我表示持续懵逼中......, 我写了这个场景,看可以不?
type Interface interface { Add(int, int) int Sub(int, int) int Mul(int, int) int } type Idem struct{} func (i Idem) Add(x int, y int) int { return x + y } func (i Idem) Sub(int, int) int { return 0 } func (i Idem) Mul(x int, _ int) int { return x * 2 }
Go语言有一个非常核心的特性:组合,上一篇文章也提及了。如果一个对象是由多个接口、匿名方法集或者对象的自身行为集构成。如果这个对象觉得有些行为是它忽略或者不想要的,这时候就可以采用上述的后两者方法去声明方法,但是这个是方法的声明,不是函数的声明。能想到的就这么多,其他我就不知道了。
函数递归
对于递归的运用,我们通过一个url,获取网页数据流,然后通过html标准库解析数据,获取各个标签元素节点。
说明: 运用html标准库解析完数据流,得到的是一个多叉树的元素节点,包括:文档节点、文本节点、元素标签节点、评论节点、注脚节点和错误节点。由这六个节点构成一颗多叉树
DEMO实例代码片段
********************************************************************** // html标准库相关说明: type Node struct { Parent, FirstChild, LastChild, PrevSibling, NextSibling *Node Type NodeType DataAtom atom.Atom Data string Namespace string Attr []Attribute } func Parse(r io.Reader) (*Node, error) ********************************************************************** var ( purl = flag.String("url", "https://www.golang.org", "website url") ) func main() { var ( resp *http.Response err error node *html.Node ) flag.Parse() if resp, err = http.Get(*purl); err != nil { log.Fatal(err.Error()) return } node, err = html.Parse(resp.Body) if err != nil { log.Fatal(err.Error()) return } resp.Body.Close() // 我们采用多叉树的深度遍历算法,并打印所有节点元素url。 for _, link := range visit(nil, node) { fmt.Println(link) } } func visit(links []string, node *html.Node) []string { if node.Type == html.ElementNode && node.Data == "a" { for _, attr := range node.Attr { links = append(links, attr.Val) } } for child := node.FirstChild; child != nil; child = child.NextSibling { links = visit(links, child) } return links }
递归元素的存储是由stack数据结构存储的。C++等主流语言的stack栈是由固定大小的,如果过多递归会导致栈内存溢出。但是Go语言的栈是动态分配的,按需分配,一般不需要考虑stack溢出安全问题,但是还是要多多尽量节约内存
大家吐槽比较多的函数返回error
在Go语言标准库使用过程中,几乎每个函数或者方法返回参数的最后一个类型都是error,类似:
func xx(sss type, ...) (xxx type, ..., err error)
比如,我们经常这样做:
*************************************************** // ioutil标准库: func ReadAll(r io.Reader) (data []byte, err error) *************************************************** if bts, err := ioutil.ReadAll(r); err !=nil{ err = errors.Wrap(err, "xxxxx") return }
大家觉得如果调用的每个方法或者函数,都要处理error,大家比较崩溃。Go语言官方是这样解释返回error的设计的:
由于对于某个应该在控制流程中处理的错误而言,将这个错误以异常的形式抛出会混乱对错误的描述,这通常会导致一些糟糕的后果。当某个程序错误被当作异常处理后,这个错误会将堆栈信息返回给用户,这些信息复杂且无用,无法帮助定位错误
所以Go语言想通过控制流if,return语句处理异常,这使得编程人员能更多的关注错误处理。
在这里给大家提供用于error追踪的包,很好用。github地址:errors
// 我一般这样使用 // 业务逻辑层 func AddSaleOrder(so *SaleOrder, o *orm.Ormer) (retCode int, err error) { if _, err = (*o).Insert(so); err != nil { err = errors.Wrap(err, "AddSaleOrder") retCode = consts.DB__INSERT_ERROR return } return } // 控制层 func (s *SaleOrderController) AddSaleOrder() { var so *models.SaleOrder ...... if retCode, err = models.AddSaleOrder(so, nil); err !=nil { Logger.Error(err.Error()) // 这里的err.Error() 会返回整个的error跟踪调用链 s.Data["json"] = map[string]interface{}{ "err_code": retCode, "err_msg": errors.Cause(err).Error(), // 这个返回err调用链中产生错误最初始的形态。 } s.ServeJSON() return } s.Data["json"] = map[string]interface{}{ "err_code": 0, "err_msg": "", } s.ServeJSON() return }
这样做的好处是:我们服务端既可以记录错误产生的整个调用链跟踪,同时,前端也返回了关键错误。屏蔽了不想要用户看到的信息。