简单来说,递归就是函数自己调用自己。有2种实现方式,一种是直接在自己函数中调用自己,一种是间接在自己函数中调用的其他函数中调用了自己。
斐波那契数列Fibonacci number:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …
如果设F(n)为该数列的第n项(n∈N*),那么这句话可以写成如下形式:F(n)=F(n-1)+F(n-2)
有F(0)=0,F(1)=1,F(2)=1,F(n)=F(n-1)+F(n-2)
循环解决斐波那契数列
package main
import "fmt"
func fib(n int) int {
switch {
case n < 0:
panic("n is negative")
case n == 0:
return 0
case n == 1 || n == 2:
return 1 //上面判断传入的n的次数
}
a, b := 1, 1
for i := 0; i < n-2; i++ {
a, b = b, a+b
}
return b // b就是输出的值
}
func main() {
for i := 1; i < 10; i++ {
fmt.Println(fib(i)) //通过循环来输出b的数值
}
}
递归实现
package main
import "fmt"
func fib(n int) int {
if n < 3 {
return 1
}
return fib(n-1) + fib(n-2) //自己调用自己
}
func main() {
for i := 1; i < 5; i++ {
fmt.Println(fib(i))
}
}
但是这个递归函数有个弊端
就是会重复计算 假设i=5的时候函数中会计算fib4和fib3
但是i=6时,函数中会计算fib5和fib4 相当于浪费计算资源 我们可以优化代码
package main
import "fmt"
func fib(n, a, b int) int {
if n < 3 {
return b
}
return fib(n-1, b, a+b)
}
func main() {
for i := 1; i < 50; i++ {
fmt.Println(fib(i, 1, 1))
}
}
n相当于循环变量
b和a+b就是每次循环体中使用的值
上面两个函数 当给i取50时,下面的结果出来时间明显短了好多
非必要不递归,因为因为递归有深度限制,再一个函数调用开销较大
func foo() {
bar()
}
func bar() {
foo()
}
foo()
间接递归调用,是函数通过别的函数调用了自己,这一样是递归。
只要是递归调用,不管是直接还是间接,都要注意边界返回问题。但是间接递归调用有时候是非常不明显,代码调用复杂时,很难发现出现了递归调用,这是非常危险的。
所有,使用良好的代码规范来避免这种递归的发生。
顾名思义,就是没有名字的函数。在函数定义中,把名字去掉就可以了
func main() {
func(x, y int) int {
result := x + y
fmt.Println(result)
return result
}(4, 5) // 定义后立即调用
}
9
func main() {
add := func(x, y int) int {
return x + y
} // 使用标识符指向一个匿名函数
fmt.Println(add(4, 5))
}
匿名函数主要作用是用作高阶函数中,它是传入的逻辑。若一个函数允许传入的参数是函数类型,就是把操作逻辑外置。
例如,给定2个整数,请给定计算函数,得到结果
package main
import "fmt"
func calc(a, b int, fn func(int, int) int) int {
return fn(a, b) // 体会calc并没有实现对a、b的操作,而是交给了fn,而fn究竟做什么操作由未来的使用者决定
}
func main() {
fmt.Println(calc(4, 5, func(a, b int) int { return a + b })) // 加法
}
9
package main
import "fmt"
type MyFunc = func(int, int) int
func calc(a, b int, fn MyFunc) int {
return fn(a, b)
}
func main() {
fmt.Println(calc(4, 5, func(a, b int) int { return a + b })) // 加法
fmt.Println(calc(4, 5, func(a, b int) int { return a * b })) // 乘法
}
package main
import "fmt"
func outer() {
c := 99
var inner = func() {
fmt.Println("1 inner", c, &c)
}
inner()
fmt.Println("2 outer", c, &c)
}
func main() {
outer()
}
1 inner 99 0xc00001a098
2 outer 99 0xc00001a098
可以看到outer中定义了另外一个函数inner,并且调用了inner。outer是包级变量,main可见,可以调用。而inner是outer中的局部变量,outer中可见。
package main
import "fmt"
func outer() {
c := 99
var inner = func() {
c = 100
fmt.Println("1 inner", c, &c)
}
inner()
fmt.Println("2 outer", c, &c)
}
func main() {
outer()
}
1 inner 100 0xc00001a098
2 outer 100 0xc00001a098
//说明内外用的同一个c声明,用的同一个标识符,也就是c是outer的局部变量,而不是inner的局部变量,当c=100时候,相当于c底层修改为了100
package main
import "fmt"
func outer() {
c := 99
var inner = func() {
c = 100
fmt.Println("1 inner", c, &c) // 请问c是多少,100
c := c + 1 //这是定义,即在当前作用域中定义新的局部变量,而这个局部变量只能影响当前作用域,不能影响其外部作用域,对外不可见
fmt.Println("3 inner", c, &c) // 请问c是多少,101
}
inner()
fmt.Println("2 outer", c, &c) // 请问c是多少 100 1,2 地址一样
}
func main() {
outer()
}
1 inner 100 0xc00001a098
3 inner 101 0xc00001a0b8
2 outer 100 0xc00001a098
**自由变量:**未在本地作用域中定义的变量。例如定义在内层函数外的外层函数的作用域中的变量。
**闭包:**就是一个概念,出现在嵌套函数中,指的是内层函数引用到了外层函数的自由变量,就形成了闭包。很多语言都有这个概念,最熟悉就是JavaScript。闭包是运行期动态的概念。函数有嵌套,
package main
import "fmt"
func outer() func() {
c := 99
fmt.Printf("outer %d %p\n", c, &c)
var inner = func() {
fmt.Printf("inner %d %p\n", c, &c)
}
return inner
}
func main() {
var fn = outer()
fn()
}
上例有闭包吗?为什么?
代码分析