Closure和Lambda

前言

文中可能會(huì)有些用刪除線標(biāo)注的內(nèi)容,之所以不刪除是個(gè)人認(rèn)為某種程度上能起一種補(bǔ)充說(shuō)明的作用。

基本概念

scope

Scoping itself is how you search for a variable with a given name.
不只是變量,而應(yīng)該還有函數(shù),所以還是用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這里不太好確定怎么翻譯,因?yàn)槭莻€(gè)現(xiàn)在分詞,百度百科將Lexical Scoping翻譯成詞法域,但是單獨(dú)拿這個(gè)Scoping翻譯成域的話則喪失了現(xiàn)在分詞代表的動(dòng)作正在進(jìn)行的意思了。

A variable has a scope which is the whole area in which that variable can be accessed by name.
變量的范圍是可以通過(guò)名稱訪問(wèn)該變量的整個(gè)區(qū)域。注意這里只是提到了變量,而沒(méi)有說(shuō)到方法。

詞法域(Lexical Scoping)

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

C/Java/Groovy/Kotlin都是lexically-scoped的語(yǔ)言,而bash就不是一個(gè)lexically-scoped的語(yǔ)言。

一個(gè)塊(block)定義一個(gè)新的域(scope),定義在一個(gè)scope內(nèi)的變量在scope外不可見(jiàn),但是scope外定義的變量在scope內(nèi)可見(jiàn),除非被覆蓋(overriden)掉,C++里有個(gè)概念叫做unqualified-name-lookup.

Lexical scoping也被稱作static scoping,靜態(tài)域或靜態(tài)作用域。

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.

在詞法作用域中,解釋器在本地函數(shù)A(現(xiàn)在運(yùn)行的函數(shù))中搜索,然后在函數(shù)A定義所在的函數(shù)B(或作用域)中搜索,然后在函數(shù)B定義所在的函數(shù)C(或作用域)中搜索。。。 這里的“詞法”是指文本,因?yàn)槟梢酝ㄟ^(guò)查看程序文本中范圍的嵌套來(lái)找出所引用的變量。(這里利用函數(shù)A、B、C來(lái)方便說(shuō)明)

百度百科把Lexical scoping稱作詞法域

動(dòng)態(tài)作用域(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.

相反,在動(dòng)態(tài)作用域的機(jī)制中,解釋器首先在本地函數(shù)中搜索,然后在調(diào)用本地函數(shù)的函數(shù)中搜索,然后在調(diào)用該函數(shù)的函數(shù)中搜索,依此回溯調(diào)用堆棧。 “動(dòng)態(tài)”是指變化,因?yàn)槊看握{(diào)用給定函數(shù)時(shí)調(diào)用堆棧都可以不同,因此函數(shù)可能會(huì)根據(jù)調(diào)用它的位置命中不同的變量。

實(shí)現(xiàn)dynamic scoping的最常見(jiàn)的方法有:深綁定(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.

深/淺綁定這兩個(gè)名詞只用于當(dāng)一個(gè)過(guò)程(或函數(shù))可以作為一個(gè)參數(shù)傳遞給一個(gè)函數(shù)的時(shí)候。

A(B()) 常規(guī)的嵌套調(diào)用

A(::B) 函數(shù)參數(shù)

深綁定在這個(gè)函數(shù)參數(shù)傳遞的時(shí)候就將其環(huán)境與其綁定。
淺綁定則直到這個(gè)參數(shù)代表的函數(shù)真正執(zhí)行的時(shí)候才將其環(huán)境與其綁定。

幫助理解的話可以看以下代碼在深綁定和淺綁定實(shí)現(xiàn)的時(shí)候,print的結(jié)果:

# 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);
};

深綁定的時(shí)候,會(huì)打印出10
淺綁定的時(shí)候,會(huì)打印出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.

閉包(也稱詞法閉包或函數(shù)閉包)是一種在函數(shù)式語(yǔ)言中實(shí)現(xiàn)詞法范圍的名稱綁定的技術(shù)。在實(shí)現(xiàn)方式上,閉包是對(duì)于函數(shù)與其執(zhí)行環(huán)境的記錄。

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.

閉包 - 不像普通函數(shù) - 允許函數(shù)通過(guò)閉包對(duì)于值或引用(閉包所在的lexical scope中的)進(jìn)行復(fù)制產(chǎn)生的副本來(lái)訪問(wèn)那些捕獲的變量,即使該函數(shù)在其作用域之外被調(diào)用。

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

閉包是有狀態(tài)的,分為捕獲閉包和非捕獲閉包

capture vs non-capture

提到這兩個(gè)詞的時(shí)候需要注意的是,non-capture并不意味著有能不能capture的閉包之分,而是不capture的閉包。

capture-by-value or capture-by-reference

不考慮實(shí)現(xiàn)方式,兩者的區(qū)別類(lèi)似于傳參機(jī)制中的pass-by-value和pass-by-reference。

什么是lambda

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

Lambda is stateless

lambda是無(wú)狀態(tài)的

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

lambda表達(dá)式: 構(gòu)造一個(gè)閉包 - 一個(gè)能夠捕獲范圍內(nèi)變量的未命名函數(shù)(匿名函數(shù))

what's lambda for ?

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

lambda讓你在java中能夠更精簡(jiǎn)地實(shí)現(xiàn)一個(gè)只有單個(gè)方法的匿名類(lèi)

兩者有什么區(qū)別

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表達(dá)式的不同之處在于閉包依賴某些變量的詞法域。因此,閉包可以捕獲并攜帶狀態(tài)。雖然lambdas是無(wú)狀態(tài)的,但閉包是有狀態(tài)的。您可以在程序中使用閉包將狀態(tài)從定義的上下文傳送到執(zhí)行點(diǎn)。

語(yǔ)言實(shí)現(xiàn)

Java

因?yàn)樵趈ava中l(wèi)ambda表達(dá)式就是匿名函數(shù),而熟悉匿名函數(shù)的應(yīng)該會(huì)知道,對(duì)于enclosing-scope中的變量需要用final修飾。

java8中得lambda表達(dá)式只能訪問(wèn)enclosing-scope中的final變量,(當(dāng)然普通的全局變量還是可以直接訪問(wèn)的),相對(duì)于舊有的final而言java8中引入了effectively-final的概念,jls中是這樣對(duì)其進(jìn)行定義的

  1. 聲明中帶有初始化的本地變量在滿足以下條件時(shí)被認(rèn)為是effectively-final
  • It is not declared final (沒(méi)被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). 從未出現(xiàn)在一個(gè)賦值表達(dá)式的左邊,注意帶初始化的本地變量的聲明語(yǔ)句不是一個(gè)賦值表達(dá)式

  • It never occurs as the operand of a prefix or postfix increment or decrement operator. 從未出現(xiàn)在自增或自減運(yùn)算符的左邊或者右邊

  1. 聲明中不進(jìn)行初始化的本地變量在滿足以下情況下被認(rèn)為是effectively-final
  • It is not declared final (沒(méi)有被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. 當(dāng)該變量出現(xiàn)在賦值表達(dá)式的左邊的時(shí)候,該變量在該賦值之前沒(méi)有賦值,而且沒(méi)有進(jìn)行定義賦值;當(dāng)該變量出現(xiàn)在賦值表達(dá)式的右邊的時(shí)候,該變量沒(méi)有被賦值并且在該賦值語(yǔ)句的右邊部分之后也沒(méi)有被定義賦值

  • It never occurs as the operand of a prefix or postfix increment or decrement operator. 從未出現(xiàn)在自增或者自減運(yùn)算符的左邊或者右邊

一個(gè)方法、構(gòu)造器、lambda或者異常的參數(shù)依照帶初始化的本地聲明變量情況來(lái)判斷是否是effectively-final

C++

有空的時(shí)候再補(bǔ)吧

Kotlin

有空的時(shí)候再補(bǔ)吧

Groovy

有空的時(shí)候再補(bǔ)吧

Python

有空的時(shí)候再補(bǔ)吧

參考

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容