〇、什么是闭包和递归
什么是闭包?
闭包就是每次调用外层函数时,临时创建的函数作用域对象。
因为内层函数作用域链中包含外层函数的作用域对象,且内层函数被引用,导致内层函数不会被释放,同时它又保持着对父级作用域的引用,这个时候就形成了闭包。所以闭包通常是在函数嵌套中形成的。
// 定义一个函数 foo(),其返回值为一个函数体
function foo (){
var name = 'Zhangsan'
return function(){
console.log('My name is '+name)
}
}
// 将 foo() 返回的函数,赋值给变量 bar
var bar = foo();
// 执行 bar(),此时执行的是 foo() 内部的函数,形成了闭包
bar(); // 输出:My name is Zhangsan
在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。变量的作用域仅限于包含它们的函数,因此无法从其它程序代码部分进行访问。不过,变量的生存期是可以很长,在一次函数调用期间所创建所生成的值在下次函数调用时仍然存在。
正因为这一特点,闭包可以用来完成信息隐藏,并进而应用于需要状态表达的某些编程范型中。详情:https://juejin.cn/post/7054205481190948894
什么是递归?
简单来说,递归表现为函数调用函数本身。
递归是一种编程技巧,它允许一个函数直接或间接地调用自身来解决问题。递归的核心思想在于将一个大问题分解为若干个小问题,然后逐步解决这些小问题,最终达到解决整个大问题的目的。
几个特点:
自身调用:原问题可以分解为子问题,子问题和原问题的求解方法是一致的,即都是调用自身的同一个函数。
终止条件:递归必须有一个终止的条件,即不能无限循环地调用本身。
简介但效率不高:递归算法解题通常显得很简洁,但递归算法解题的运行效率较低,因为数据存入堆栈中,等待函数调用结束后再取出,会增加性能消耗。另外堆栈还存在溢出的风险。
具有以下特征的问题可考虑递归求解:当问题和子问题具有递推关系,比如杨辉三角、计算阶乘等;具有递归性质的数据结构,比如链表、树、图等;反向性问题,比如取反。总结下来,最根本的还是要抓住问题本身是否可以通过层层拆解到最小粒度来得解。
总的来说,递归算法是一种解决问题的方法,虽然可以使代码简洁,但执行效率较低,且递归次数过多可能导致栈溢出。因此,在业务环境比较复杂的场景不太适用,业务逻辑简单的场景可以考虑。
一、Go 中的闭包
Go 语言是支持闭包的,这里只是简单地讲一下在 Go 语言中闭包是如何实现的。下面我来将之前的 JavaScript 的闭包例子用 Go 来实现。
package main
import (
"fmt"
)
func a() func() {
name := "Zhangsan"
return func() {
fmt.Println("My name is " + name)
}
}
func main() {
c := a()
c() // 输出:My name is Zhangsan
}
闭包复制的是原对象指针,这就很容易解释延迟引用现象。延迟引用:也称为惰性求值或懒惰计算,是一种程序执行策略,其中表达式的求值被推迟到其实际需要时才进行。这意味着,如果一个表达式的结果没有被立即使用,或者根本不需要,那么该表达式就不会被求值。
如下示例代码,两次引用的 x 是同一个地址:
package main
import "fmt"
func test() func() {
x := 100
fmt.Printf("1:x (%p) = %d\n", &x, x)
return func() {
fmt.Printf("2:x (%p) = %d\n", &x, x)
}
}
func main() {
f := test() // 1:x (0xc000096068) = 100
f() // 2:x (0xc000096068) = 100
}
在汇编层 ,test 实际返回的是 FuncVal 对象,其中包含了匿名函数地址、闭包对象指针。当调 匿名函数时,只需以某个寄存器传递该对象即可。
FuncVal { func_address, closure_var_pointer ... }
既然引用的是同一个地址,那么操作就是连续的,比如说加减法。下面是一个简单的示例:
package main
import "fmt"
// 返回 2 个函数类型的返回值,都是在同一个变量 base 基础上进行操作
func test01(base int) (func(int) int, func(int) int) {
// 定义2个函数,并返回
add := func(i int) int { // 相加
base += i
return base
}
sub := func(i int) int { // 相减
base -= i
return base
}
return add, sub // 返回
}
func main() {
f1, f2 := test01(10)
fmt.Println(f1(1), f2(2)) // 11 9
fmt.Println(f1(3), f2(4)) // 12 8
}
// 10+1=11 11-2=9
// 9+3=12 12-4=8
那么,如果调用两次 test() 函数,就是复制了不同的地址。如下示例代码,分别用 100 和 200 调用:
package main
import "fmt"
func test(x int) func() {
// x = 100
fmt.Printf("1:x (%p) = %d\n", &x, x)
return func() {
fmt.Printf("2:x (%p) = %d\n", &x, x)
}
}
func main() {
fmt.Print("f()---------\n")
f := test(100)
f()
fmt.Print("ff()---------\n")
ff := test(200)
ff()
fmt.Print("f()---------\n")
f()
}
// 输出:
// f()---------
// 1:x (0xc00000a0c8) = 100
// 2:x (0xc00000a0c8) = 100
// ff()---------
// 1:x (0xc00000a100) = 200
// 2:x (0xc00000a100) = 200
// f()---------
// 2:x (0xc00000a0c8) = 100
有输出结果可以知,两次调用分别引用的是不同的变量地址。
二、Go 中的递归
此部分就通过实现两个关于递归的经典案例,来看下 Go 中的递归是如何实现的。
数字阶乘
一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且 0 的阶乘为 1。自然数 n 的阶乘写作 n!。1808 年,基斯顿·卡曼引进这个表示法。
package main
import "fmt"
func factorial(i int) int {
if i <= 1 {
return 1
}
return i * factorial(i-1)
}
func main() {
var i int = 4
fmt.Printf("Factorial of %d is %d\n", i, factorial(i)) // 输出:Factorial of 4 is 24
}
// 1*2*3*4=24
斐波那契数列(Fibonacci)
这个数列从第 3 项开始,每一项都等于前两项之和。
package main
import "fmt"
func fibonaci(i int) int {
if i == 0 {
return 0
}
if i == 1 {
return 1
}
return fibonaci(i-1) + fibonaci(i-2)
}
func main() {
var i int
for i = 0; i < 10; i++ {
fmt.Printf("%d\n", fibonaci(i))
}
}
// 输出:
// 0
// 1
// 1
// 2
// 3
// 5
// 8
// 13
// 21
// 34
参考:http://www.topgoer.com/%E5%87%BD%E6%95%B0/%E9%97%AD%E5%8C%85%E9%80%92%E5%BD%92.html
标签:闭包,函数,递归,int,fmt,Go,func,GO From: https://www.cnblogs.com/hnzhengfy/p/go_bibaodigui.html