Closure和Lambda

前言

文中可能会有些用删除线标注的内容,之所以不删除是个人认为某种程度上能起一种补充说明的作用。

基本概念

scope

Scoping itself is how you search for a variable with a given name.
不只是变量,而应该还有函数,所以还是用wikipedia的解释
In computer programming, the 'scope' of a name binding - an association of a name to an entity, such as a variable – is the region of a computer program where the binding is valid: where the name can be used to refer to the entity.

Scoping这里不太好确定怎么翻译,因为是个现在分词,百度百科将Lexical Scoping翻译成词法域,但是单独拿这个Scoping翻译成域的话则丧失了现在分词代表的动作正在进行的意思了。

A variable has a scope which is the whole area in which that variable can be accessed by name.
变量的范围是可以通过名称访问该变量的整个区域。注意这里只是提到了变量,而没有说到方法。

词法域(Lexical Scoping)

Scope rules define the visibility rules for names in a programming language.

C/Java/Groovy/Kotlin都是lexically-scoped的语言,而bash就不是一个lexically-scoped的语言。

一个块(block)定义一个新的域(scope),定义在一个scope内的变量在scope外不可见,但是scope外定义的变量在scope内可见,除非被覆盖(overriden)掉,C++里有个概念叫做unqualified-name-lookup.

Lexical scoping也被称作static scoping,静态域或静态作用域。

In lexical scoping, the interpreter search in the local function (the function which is running now), then you search in the function (or scope) in which that function was defined, then you search in the function (scope) in which that function was defined, and so forth. "Lexical" here refers to text, in that you can find out what variable is being referred to by looking at the nesting of scopes in the program text.

在词法作用域中,解释器在本地函数A(现在运行的函数)中搜索,然后在函数A定义所在的函数B(或作用域)中搜索,然后在函数B定义所在的函数C(或作用域)中搜索。。。 这里的“词法”是指文本,因为您可以通过查看程序文本中范围的嵌套来找出所引用的变量。(这里利用函数A、B、C来方便说明)

百度百科把Lexical scoping称作词法域

动态作用域(Dynamic Scoping)

In dynamic scoping, by contrast, the interpreter search in the local function first, then it search in the function that called the local function, then it search in the function that called that function, and so on, up the call stack. "Dynamic" refers to change, in that the call stack can be different every time a given function is called, and so the function might hit different variables depending on where it is called from.

相反,在动态作用域的机制中,解释器首先在本地函数中搜索,然后在调用本地函数的函数中搜索,然后在调用该函数的函数中搜索,依此回溯调用堆栈。 “动态”是指变化,因为每次调用给定函数时调用堆栈都可以不同,因此函数可能会根据调用它的位置命中不同的变量。

实现dynamic scoping的最常见的方法有:深绑定(deep binding)和浅绑定(shallow binding)

绑定

Deep/shallow binding makes sense only when a procedure can be passed as an argument to a function.
Deep binding binds the environment at the time a procedure is passed as an argument.
Shallow binding binds the environment at the time a procedure is actually called.

深/浅绑定这两个名词只用于当一个过程(或函数)可以作为一个参数传递给一个函数的时候。

A(B()) 常规的嵌套调用

A(::B) 函数参数

深绑定在这个函数参数传递的时候就将其环境与其绑定。
浅绑定则直到这个参数代表的函数真正执行的时候才将其环境与其绑定。

帮助理解的话可以看以下代码在深绑定和浅绑定实现的时候,print的结果:

# https://stackoverflow.com/questions/15550648/shallow-deep-binding-what-would-this-program-print
function f1() {
    var x = 10;
    function f2(fx)
    {
        var x;
        x = 6;
        fx();
    };

    function f3()
    {
        print x;
    };

    f2(f3);
};

深绑定的时候,会打印出10
浅绑定的时候,会打印出6

什么是Closure

A closure (also lexical closure or function closure) is a technique for implementing lexically scoped name binding in a language with first-class functions. Operationally, a closure is a record storing a function together with an environment.

闭包(也称词法闭包或函数闭包)是一种在函数式语言中实现词法范围的名称绑定的技术。在实现方式上,闭包是对于函数与其执行环境的记录。

A closure -unlike a plain function- allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.

闭包 - 不像普通函数 - 允许函数通过闭包对于值或引用(闭包所在的lexical scope中的)进行复制产生的副本来访问那些捕获的变量,即使该函数在其作用域之外被调用。

Closure is state-full, capture or non-capture.

闭包是有状态的,分为捕获闭包和非捕获闭包

capture vs non-capture

提到这两个词的时候需要注意的是,non-capture并不意味着有能不能capture的闭包之分,而是不capture的闭包。

capture-by-value or capture-by-reference

不考虑实现方式,两者的区别类似于传参机制中的pass-by-value和pass-by-reference。

什么是lambda

Lambda comes from the Lambda Calculus and refers to anonymous functions in programming.

Lambda is stateless

lambda是无状态的

cppreference中是这样描述lambda expression的:
Constructs a closure: an unnamed function object capable of capturing variables in scope.

lambda表达式: 构造一个闭包 - 一个能够捕获范围内变量的未命名函数(匿名函数)

what's lambda for ?

In java , lambda let you express instances of single-method classes more compactly.

lambda让你在java中能够更精简地实现一个只有单个方法的匿名类

两者有什么区别

Closures differ from lambda expressions in that they rely on their lexical scope for some variables. As a result, closures can capture and carry state with them. While lambdas are stateless, closures are stateful. You can use them in your programs to carry state from a defining context to the point of execution.

闭包与lambda表达式的不同之处在于闭包依赖某些变量的词法域。因此,闭包可以捕获并携带状态。虽然lambdas是无状态的,但闭包是有状态的。您可以在程序中使用闭包将状态从定义的上下文传送到执行点。

语言实现

Java

因为在java中lambda表达式就是匿名函数,而熟悉匿名函数的应该会知道,对于enclosing-scope中的变量需要用final修饰。

java8中得lambda表达式只能访问enclosing-scope中的final变量,(当然普通的全局变量还是可以直接访问的),相对于旧有的final而言java8中引入了effectively-final的概念,jls中是这样对其进行定义的

  1. 声明中带有初始化的本地变量在满足以下条件时被认为是effectively-final
  • It is not declared final (没被final修饰)

  • It never occurs as the left hand side in an assignment expression (Note that the local variable declarator containing the initializer is not an assignment expression). 从未出现在一个赋值表达式的左边,注意带初始化的本地变量的声明语句不是一个赋值表达式

  • It never occurs as the operand of a prefix or postfix increment or decrement operator. 从未出现在自增或自减运算符的左边或者右边

  1. 声明中不进行初始化的本地变量在满足以下情况下被认为是effectively-final
  • It is not declared final (没有被final修饰)

  • Whenever it occurs as the left hand side in an assignment expression, it is definitely unassigned and not definitely assigned before the assignment; that is, it is definitely unassigned and not definitely assigned after the right hand side of the assignment expression. 当该变量出现在赋值表达式的左边的时候,该变量在该赋值之前没有赋值,而且没有进行定义赋值;当该变量出现在赋值表达式的右边的时候,该变量没有被赋值并且在该赋值语句的右边部分之后也没有被定义赋值

  • It never occurs as the operand of a prefix or postfix increment or decrement operator. 从未出现在自增或者自减运算符的左边或者右边

一个方法、构造器、lambda或者异常的参数依照带初始化的本地声明变量情况来判断是否是effectively-final

C++

有空的时候再补吧

Kotlin

有空的时候再补吧

Groovy

有空的时候再补吧

Python

有空的时候再补吧

参考

Scope

Using closures to capture state

Lexical and Dynamic Scoping

Closure

DynamicBinding vs LexicalBinding

Lambda Expressions

java 8 lambda limitations: Closures

Dynamic Scoping

The Art Of The Interpreter

The Original 'Lambda Papers' by Guy Steele and Gerald Sussman

A lambda is not necessary a closure

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容