一 闭包详解

闭包的应该都听过,但到底什么是闭包呢?

闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。

“官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

维基百科讲,闭包(Closure),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

看着上面的描述,会发现闭包和匿名函数似乎有些像。可是可能还是有些云里雾里的。因为跳过闭包的创建过程直接理解闭包的定义是非常困难的。目前在JavaScript、Go、PHP、Scala、Scheme、Common Lisp、Smalltalk、Groovy、Ruby、 Python、Lua、objective c、Swift 以及Java8以上等语言中都能找到对闭包不同程度的支持。通过支持闭包的语法可以发现一个特点,他们都有垃圾回收(GC)机制。

在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。变量的作用域仅限于包含它们的函数,因此无法从其它程序代码部分进行访问。不过,变量的生存期是可以很长,在一次函数调用期间所创建所生成的值在下次函数调用时仍然存在。正因为这一特点,闭包可以用来完成信息隐藏,并进而应用于需要状态表达的某些编程范型中。

二 Go的闭包

Go语言是支持闭包的,这里只是简单地讲一下在Go语言中闭包是如何实现的。 下面我来将闭包例子用Go来实现。

package main

import (
"fmt"
) func a() func() int {
i := 0
b := func() int {
i++
fmt.Println(i)
return i
}
return b
} func main() {
c := a()
c()
c()
c() a() //不会输出i
}

输出结果:

    1
2
3

可以发现,输出和之前的JavaScript的代码是一致的。具体的原因和上面的也是一样的,这说明Go语言是支持闭包的。

闭包复制的是原对象指针,这就很容易解释延迟引用现象。

package main

import "fmt"

func test() func() {
x := 100
fmt.Printf("x (%p) = %d\n", &x, x) return func() {
fmt.Printf("x (%p) = %d\n", &x, x)
}
} func main() {
f := test()
f()
}

输出:

    x (0xc42007c008) = 100
x (0xc42007c008) = 100

在汇编层 ,test 实际返回的是 FuncVal 对象,其中包含了匿名函数地址、闭包对象指针。当调 匿名函数时,只需以某个寄存器传递该对象即可。

    FuncVal { func_address, closure_var_pointer ... }

外部引用函数参数局部变量

package main

import "fmt"

// 外部引用函数参数局部变量
func add(base int) func(int) int {
return func(i int) int {
base += i
return base
}
} func main() {
tmp1 := add(10)
fmt.Println(tmp1(1), tmp1(2))
// 此时tmp1和tmp2不是一个实体了
tmp2 := add(100)
fmt.Println(tmp2(1), tmp2(2))
}

返回2个闭包

package main

import "fmt"

// 返回2个函数类型的返回值
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)
// base一直是没有消
fmt.Println(f1(1), f2(2))
// 此时base是9
fmt.Println(f1(3), f2(4))
}

三 Go 语言递归函数

递归,就是在运行的过程中调用自己。 一个函数调用自己,就叫做递归函数。

构成递归需具备的条件:

    1.子问题须与原始问题为同样的事,且更为简单。
2.不能无限制地调用本身,须有个出口,化简为非递归状况处理。

数字阶乘

一个正整数的阶乘(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 = 7
fmt.Printf("Factorial of %d is %d\n", i, factorial(i))
}

输出结果:

    Factorial of 7 is 5040

斐波那契数列(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

Go中的闭包、递归的更多相关文章

  1. [译] Closures in Lua - Lua中的闭包

    原文:(PDF) . 摘要 一等(first-class)函数是一种非常强大的语言结构,并且是函数式语言的基础特性.少数过程式语言由于其基于栈的实现,也支持一等函数.本文讨论了Lua 5.x用于实现一 ...

  2. 浅谈JS中的闭包

    浅谈JS中的闭包 在介绍闭包之前,我先介绍点JS的基础知识,下面的基础知识会充分的帮助你理解闭包.那么接下来先看下变量的作用域. 变量的作用域 变量共有两种,一种为全局变量,一种为局部变量.那么全局变 ...

  3. [转][译] Closures in Lua - Lua中的闭包

    http://www.cnblogs.com/plodsoft/p/5900270.html?utm_source=tuicool&utm_medium=referral 原文:(PDF) . ...

  4. javaScript中的闭包原理 (译)

    这篇文章通过javaScript代码解释了闭包的原理,来让编程人员理解闭包.它不是写给大牛或使用功能性语言进行编程的程序员的.一旦意会了其核心概念,闭包理解起来并不难.然而,你不可能通过阅读任何有关闭 ...

  5. 让你分分钟学会Javascript中的闭包

    Javascript中的闭包 前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它 ...

  6. 【原】理解javascript中的闭包

    闭包在javascript来说是比较重要的概念,平时工作中也是用的比较多的一项技术.下来对其进行一个小小的总结 什么是闭包? 官方说法: 闭包是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见 ...

  7. 难道这就是JavaScript中的"闭包"

    其实对于JavaScript中的"闭包"还没真正理解,这次在实际Coding中似乎遇到了"闭包"的问题,仅此摘录,以待深究. 表现为jQuery的post方法回 ...

  8. 【原】如何在jQuery中实现闭包

    原生JS中,闭包虽好用,但是很难用好,在jQuery中一样,都有一些点需要我们注意.jQuery中使用闭包的常见情况有以下几种: 1.$(document).ready()的参数 我们在写jQuery ...

  9. 说说Python中的闭包 - Closure

    转载自https://segmentfault.com/a/1190000007321972 Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都需要去了解这么一个东西 ...

  10. 浅谈JavaScript中的闭包

    浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...

随机推荐

  1. 【Java线程池】 java.util.concurrent.ThreadPoolExecutor 分析

    线程池概述 线程池,是指管理一组同构工作线程的资源池. 线程池在工作队列(Work Queue)中保存了所有等待执行的任务.工作者线程(Work Thread)会从工作队列中获取一个任务并执行,然后返 ...

  2. linux 常用操作搜集

    1.去除空行 方法一:利用grep grep -v '^\s*$' test.txt 注:-v表示将匹配的结果进行反转,正则表达式匹配空行.(空行可包括空格符制表符等空白字符) 方法二:利用sed s ...

  3. Java学习 (七)基础篇 变量

    变量 变量顾名思义,就是可以变化的量 Java是一种强类型语言,每个变量都必须声明其类型 Java变量是程序中最基本的存储单位,其要素包括变量名.变量类型和作用域 type varName [=val ...

  4. 小白之Python基础(一)

    一.数字类型: 1.整形 十进制:默认为十进制:(如:99,100.......) 十六进制: 0x,0X开头的表示16进制数 二进制:0b,0B开头的表示2进制数 八进制: 0o,0O开头的表示8进 ...

  5. 那些舍不得删除的 MP3--批量修改mp3的ID3tag

    整理电脑时发现很多mp3.那是大约2001年至2009年之间.那个时候大家听歌,还是习惯从网上下载mp3.虽然现在听歌比从前方便多了,简单到只需在APP中输入歌名,但用播放器听mp3的感觉是完全不同的 ...

  6. 万答#13,MySQL自增键用完后,插入数据会发生什么情况

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 MySQL自增键用完了,插入数据会发生什么情况 1.实验场景 GreatSQL ...

  7. 最新豆瓣top250爬虫案例代码分析[注释齐全]

    导入包 # json包 import json #正则表达式包 import re import requests from requests import RequestException 定义爬取 ...

  8. react实战 系列 —— React 的数据流和生命周期

    其他章节请看: react实战 系列 数据流和生命周期 如何处理 React 中的数据,组件之间如何通信,数据在 React 中如何流动? 常用的 React 生命周期方法以及开源项目 spug 中使 ...

  9. 统计 Word 文档字数的方式

    描述 欲统计某文档的字数,有两种方式. "审阅"选项卡--"校对"组--字符统计 点击左下角字数统计 审阅查看字数 此步骤较为复杂,在审阅选项卡中可以查询文档的 ...

  10. Android Studio 模拟器(AVD)访问互联网

    模拟器默认是不可以直接访问互联网的,需要为模拟器配置 DNS 服务器. (一)找到模拟器安装的位置 模拟器安装位置在安卓 SDK 下面,进入[SDK Path]/emulator. (二)打开终端输入 ...