参考:
前言
文中可能会有些用删除线标注的内容,之所以不删除是个人认为某种程度上能起一种补充说明的作用。
基本概念
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,静态域或静态作用域。
在词法作用域中,解释器在本地函数A(现在运行的函数)中搜索,然后在函数A定义所在的函数B(或作用域)中搜索,然后在函数B定义所在的函数C(或作用域)中搜索。。。 这里的“词法”是指文本,因为您可以通过查看程序文本中范围的嵌套来找出所引用的变量。(这里利用函数A、B、C来方便说明)
百度百科把Lexical scoping称作词法域
动态作用域(Dynamic Scoping)
相反,在动态作用域的机制中,解释器首先在本地函数中搜索,然后在调用本地函数的函数中搜索,然后在调用该函数的函数中搜索,依此回溯调用堆栈。 “动态”是指变化,因为每次调用给定函数时调用堆栈都可以不同,因此函数可能会根据调用它的位置命中不同的变量。
实现dynamic scoping的最常见的方法有:深绑定(deep binding)和浅绑定(shallow binding)
绑定
深/浅绑定这两个名词只用于当一个过程(或函数)可以作为一个参数传递给一个函数的时候。
A(B()) 常规的嵌套调用
A(::B) 函数参数
深绑定在这个函数参数传递的时候就将其环境与其绑定。
浅绑定则直到这个参数代表的函数真正执行的时候才将其环境与其绑定。
帮助理解的话可以看以下代码在深绑定和浅绑定实现的时候,print的结果:
# https://*.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
闭包(也称词法闭包或函数闭包)是一种在函数式语言中实现词法范围的名称绑定的技术。在实现方式上,闭包是对于函数与其执行环境的记录。
闭包 - 不像普通函数 - 允许函数通过闭包对于值或引用(闭包所在的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中能够更精简地实现一个只有单个方法的匿名类
两者有什么区别
闭包与lambda表达式的不同之处在于闭包依赖某些变量的词法域。因此,闭包可以捕获并携带状态。虽然lambdas是无状态的,但闭包是有状态的。您可以在程序中使用闭包将状态从定义的上下文传送到执行点。
语言实现
Java
因为在java中lambda表达式就是匿名函数,而熟悉匿名函数的应该会知道,对于enclosing-scope中的变量需要用final修饰。
java8中得lambda表达式只能访问enclosing-scope中的final变量,(当然普通的全局变量还是可以直接访问的),相对于旧有的final而言java8中引入了effectively-final的概念,jls中是这样对其进行定义的
- 声明中带有初始化的本地变量在满足以下条件时被认为是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. 从未出现在自增或自减运算符的左边或者右边
- 声明中不进行初始化的本地变量在满足以下情况下被认为是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
有空的时候再补吧
参考
Using closures to capture state
DynamicBinding vs LexicalBinding
java 8 lambda limitations: Closures
The Original 'Lambda Papers' by Guy Steele and Gerald Sussman
A lambda is not necessary a closure