c语言log函数_Go语言2:函数作为参数传递与闭包的实现

在冯诺伊曼体系结构中,数据和代码在程序运行的时候都会加载到内存中,内存本身可读写,代码和数据本身也是没有区别的,都是一串二进制,只是一些二进制是交给CPU去执行的,一些二进制只是作为数据含义,在不同的场景表示不同的数据。因此,从这个角度来讲,函数能传一个内存数据的地址,也自然能够传递一个函数的地址。传递函数地址并且执行函数本身也变得很自然并且容易理解了。

Go语言宣称函数是一等公民,最重要的就是函数能够像普通的参数一样可以作为返回值,也可以作为参数传递,而不是像Java一样lambda表达式只是一种语法糖一样的东西,传递的还是对象。

下面使用C语言中的函数指针,来完成传递函数or返回函数的demo。

#include //demo 忽略参数检查//定义一个 (int,int)->int 这样函数的指针 类型别名为IntBiFunctiontypedef int(*IntBiFunction)(int,int);//两数相加int add(int,int);//两数相减int sub(int,int);int reduce(int* array,int size,IntBiFunction fun,int initial);int main(int argv,char **args){    int array[] = {1,2,3,4,5};    int cumulate = reduce(array,5,add,0);    int subtract = reduce(array,5,sub,0);    printf("累加结果: %d\n",cumulate);    printf("累减结果: %d\n",subtract);}int add(int x,int y){    return x+y;}int sub(int x,int y){    return x-y;}int reduce(int* array,int size,IntBiFunction fun,int initial){    int i = 0;    while (size-->0){        initial = fun(initial,array[i++]);    }    return initial;}

以上模拟了一个简单的传入不同的函数实现不同reduce结果的代码段。这里可以发现函数指针其实是一个封装算法的好手,抽象的逻辑与具体的实现分离开来,也自然可以联想到面向接口编程,也自然可以想到,面向对象中的多态特性,是可以通过函数指针实现的。C++中的多态实现中虚函数表中存的也是一个个的函数指针。

现在再了解下闭包。我们先用JavaScript语言写一个闭包的demo,再去说明闭包是什么。他们有什么用?再Go中是怎么实现的。以及其他语言如Java中闭包的概念。

counter = (function(){  var times = 1;  return function(){    return ++times;  }})()//1console.log(counter())//2console.log(counter())//3console.log(counter())

以上是使用JavaScript语言编写的一个简单的计数器,执行多少次函数返回就是多少,这个counter函数最终是通过一个函数返回得到的,也就是说返回值是函数。只不过这个返回值函数引用了一个外部函数作用域中的一个变量times,来完成计数的操作,不过也正因为其在函数作用域中,因此在全局的作用域中也看不到这个变量,达到了变量隐藏的目的,这就是闭包,下面看下官方的定义(摘自维基百科)。

73ebffe05f80df3374a514b9e773fe94.png

其中一个函数,和一个关联的环境相结合,就是闭包的实现原理。这个实现方式稍后解释,我们先将JavaScript的小demo迁移到Go上。

package mainimport (  "fmt")func main() {  //通过一个匿名函数返回另外一个函数构成一个函数闭包  counter:=(func () func() int {    times:=0    return func() int {      //注意直接return times++ 不可以,因为和其他C like 语言不一样      //Go认为times++是一个语句,而不是一个表达式,这是一个值得注意的区别      times++      return times    }  })()  fmt.Printf("执行次数: %d\n",counter())  fmt.Printf("执行次数: %d\n",counter())  fmt.Printf("执行次数: %d\n",counter())}

以上简单介绍了闭包,下面从闭包的实现角度分析。

在上面提到,闭包是一个函数+对应绑定的变量,这些变量我们成为捕获变量。而函数指针可以指向具体的函数,因此,函数指针和捕获变量构成的二元组作为一个独立的结构,每次访问函数的时候使用捕获变量,就可以实现简单的闭包了。

调用能生成闭包的函数,会在堆空间生成一个闭包结构体,里面包含了函数指针以及捕获变量的列表,而具体哪些变量需要捕获是编译器决定的。而每次调用函数,都是通过先找到这样的结构体来访问的。

在Java中,使用Lambda表达式或者使用内部类访问外部函数作用域中的变量也构成了闭包,不过闭包所绑定的本地变量必须使用final修饰符。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值