爱上开源之golang入门至实战第四章函数(Func)(四)
4.4.4 命名的返回值
GO语言函数声明的返回值除了支持多个返回值这个不同于其他语言的特点;还支持返回值命名的特点;来看一个例子
func Fibonacci(a int) int {
if a == 0 {
return 0
}
if a == 1 {
return 1
}
return Fibonacci(a-2) + Fibonacci(a-1)
}
a := 3
fmt.Printf("Fibonacci(%d)=%d \n", a, Fibonacci(a))
a = 4
fmt.Printf("Fibonacci(%d)=%d \n", a, Fibonacci(a))
a = 5
fmt.Printf("Fibonacci(%d)=%d \n", a, Fibonacci(a))
==== OUTPUT ====
Fibonacci(3)=2
Fibonacci(4)=3
Fibonacci(5)=5
这是一个很普通的函数, 函数计算斐波那契数列(Fibonacci)的元素对应值;在这个函数里,返回值只有一个int的值,我们可以将上述Fibonacci函数改写为带命名的返回值的方式
func FibonacciA(a int) (n int) {
if a <= 0 {
n = 0
}
if a == 1 {
n = 1
}
if a >= 2 {
n = Fibonacci(a-2) + Fibonacci(a-1)
}
return
}
使用命名返回值的方式的时候;可以把命名返回值作为结果返回形参(result parameters)的形式;简而言之就是完全可以把命名返回值当作是一个参数这样的方式来使用;和传入参数的方式一样, 基础类型都是值传递;指针类型参数都是以引用传递;当出现命名方悔之,命名返回值被初始化为相应类型的零值,当需要返回的时候,我们只需要一 条简单的不带参数的return语句。需要注意的是,即使只有一个命名返回值,在定义的时候也需要使用 () 括起来:比如例子里出现的就是一个返回值的(n int);
上面的例子,可以改写为
func FibonacciB(a int) (n int) {
if a <= 0 {
n = 0
return
}
if a == 1 {
n = 1
return
}
n = Fibonacci(a-2) + Fibonacci(a-1)
return
}
下一个例子,我们进一步升级上面的
我们可以通过斐波那契数列(Fibonacci)的函数, 目前的斐波那契数列(Fibonacci)返回是对于元素的值, 现在升级一下,返回整个队列的所有元素。
func FibonacciC(a int) (n []int) {
if a <= 0 {
n = append(n, 0)
return
}
if a == 1 {
n = append(FibonacciC(a-1), 1)
return
}
n = append(FibonacciC(a - 1))
n = append(n, n[len(n)-1]+n[len(n)-2])
return
}
a := 0
fmt.Printf("Fibonacci(%d)=%v \n", a, FibonacciC(a))
a = 1
fmt.Printf("Fibonacci(%d)=%v \n", a, FibonacciC(a))
a = 2
fmt.Printf("Fibonacci(%d)=%v \n", a, FibonacciC(a))
a = 3
fmt.Printf("Fibonacci(%d)=%v \n", a, FibonacciC(a))
a = 4
fmt.Printf("Fibonacci(%d)=%v \n", a, FibonacciC(a))
a = 5
fmt.Printf("Fibonacci(%d)=%v \n", a, FibonacciC(a))
==== OUTPUT ====
Fibonacci(0)=[0]
Fibonacci(1)=[0 1]
Fibonacci(2)=[0 1 1]
Fibonacci(3)=[0 1 1 2]
Fibonacci(4)=[0 1 1 2 3]
Fibonacci(5)=[0 1 1 2 3 5]
可以看到这个列子就能够充分看到带命名的返回参数,所带来的语言之美,不仅仅是省略了一个返回值的定义的过程,这里的返回值和参数一样的性质,值传递,还是引用传递,Go适合于较低层的服务的实现,恰恰就是这些特性非常符合低层服务的编程习惯。 再来升级一下咱们的例子斐波那契数列(Fibonacci)的函数, 关于Error的应用,
func FibonacciD(a int) (n []int, err error) {
if a < 0 {
err = errors.New("illegal Argument")
return
}
if a == 0 {
n = append(n, 0)
return
}
if a == 1 {
n = append(FibonacciC(a-1), 1)
return
}
n = append(FibonacciC(a - 1))
n = append(n, n[len(n)-1]+n[len(n)-2])
return
}
a := 0
if n, err := FibonacciD(a); err == nil {
fmt.Printf("Fibonacci(%d)=%v \n", a, n)
} else {
fmt.Printf("Fibonacci(%d)=%v \n", a, err)
}
a = 1
if n, err := FibonacciD(a); err == nil {
fmt.Printf("Fibonacci(%d)=%v \n", a, n)
} else {
fmt.Printf("Fibonacci(%d)=%v \n", a, err)
}
a = 2
if n, err := FibonacciD(a); err == nil {
fmt.Printf("Fibonacci(%d)=%v \n", a, n)
} else {
fmt.Printf("Fibonacci(%d)=%v \n", a, err)
}
a = 3
if n, err := FibonacciD(a); err == nil {
fmt.Printf("Fibonacci(%d)=%v \n", a, n)
} else {
fmt.Printf("Fibonacci(%d)=%v \n", a, err)
}
a = 4
if n, err := FibonacciD(a); err == nil {
fmt.Printf("Fibonacci(%d)=%v \n", a, n)
} else {
fmt.Printf("Fibonacci(%d)=%v \n", a, err)
}
a = 5
if n, err := FibonacciD(a); err == nil {
fmt.Printf("Fibonacci(%d)=%v \n", a, n)
} else {
fmt.Printf("Fibonacci(%d)=%v \n", a, err)
}
a = -1
if n, err := FibonacciD(a); err == nil {
fmt.Printf("Fibonacci(%d)=%v \n", a, n)
} else {
fmt.Printf("Fibonacci(%d)=%v \n", a, err)
}
==== OUTPUT ====
Fibonacci(0)=[0]
Fibonacci(1)=[0 1]
Fibonacci(2)=[0 1 1]
Fibonacci(3)=[0 1 1 2]
Fibonacci(4)=[0 1 1 2 3]
Fibonacci(5)=[0 1 1 2 3 5]
Fibonacci(-1)=illegal Argument
在Go里,没有Java语言里那么强大方便的Exception Stack的处理机制,Java的Exception Stack的处理机制非常方便的对Exception进行处理,只要是熟悉Exception机制,就可以通过Throw Exception和Catch Exception来对Exception进行处理, 虽然方便,但是由于Expceptoin Stack毕竟是以内存Stack的模式来保持的,所以不管是在内存损耗,还是性能损耗上,还是存在损耗的,虽然JAVA JVM一直在优化,但机制如此,不管如何优化损耗依然存在,这就是以往JAVA程序员在谈到性能优化的时候,关于exception方面的优化,也可以提出很多的条目来就是如此。 在Go语言里没有这个Exception Stack,对于JAVA语言诞生而言,其就是关注与服务器端的应用业务, 而Go的诞生是来作为拥抱云原生的服务组件,可能exceptoin的损耗反馈到业务系统里可以忽略不计,但是对于面向低层的服务,这些损耗不能不计。 Exception stack的设计是面向应用的,JAVA体系很多架构(EJB,Spring,MyBatis)就明确区分了系统异常和应用异常。都是基于Excepition Stack的,
对于Go语言来说,也可以通过应用框架做到这样的效果,但是对于Go语言的本身而言,实际上是性能考虑和应用考虑的权衡选择。并不是像很多Go初学者而言,不支持。Go里完全可以通过defer/recover来实现。只是这个明显是应用架构需要关心的东西, 就不需要放在go的语言架构上支持了。比较对于Go所面对的大多数场景,性能上的优秀是考虑的要点。