2013-04-25 20 views
16
var x = 1; 
    Func<int,int> f = y => x + y; 
    x = 2; 
    Console.WriteLine(f(1)); 

輸出爲3.我會假設這是2,根據http://www.cs.cornell.edu/~clarkson/courses/csci4223/2013sp/lec/lec12.pdf我認爲C#具有詞法範圍,但爲什麼這個例子顯示動態範圍行爲?

+0

您在調用'f'之前更改了'x'的值。你爲什麼不期望使用'x'的當前值? – 2013-04-25 17:07:27

+6

爲什麼4?也許你認爲2? – 2013-04-25 17:08:00

+0

爲什麼4?我看你怎麼會認爲f(1)= 2,但是你會得到4嗎? – jure 2013-04-25 17:08:34

回答

21

有關於詞彙範圍是PDF並不能完全解釋一個細微之處。它的例子實際上有兩個不同的變量,名稱爲x,它不重新分配第一個x的值(事實上功能語言可能不允許變化)。

C#在詞彙範圍內 - 它在lambda的定義點上查找x,而不是調用委託時。但是:x解析爲一個變量,而不是一個值,它在調用時讀取該變量的值。

下面是一個更完整的示例:

int InvokeIt(Func<int, int> f) 
{ 
    int x = 2; 
    return f(1); 
} 

Func<int, int> DefineIt() 
{ 
    int x = 1; 
    Func<int, int> d = (y => x + y); 
    x = 3; // <-- the PDF never does this 
    return d; 
} 

Console.WriteLine(InvokeIt(DefineIt())); 

拉姆達結合存在內部DefineItx可變。定義點的值(x = 1)無關緊要。 變量稍後設置爲x = 3

但是顯然不是動態範圍要麼是因爲x = 2裏面沒有使用InvokeIt

+0

對不起,這是傷害我的大腦,我試圖運行您的示例來了解發生了什麼,但我得到的編譯器錯誤,DefineIt()不返回一個值。沒關係,你更新了它。 – BlackICE 2013-04-25 17:22:49

+0

@BlackICE:對不起,現在修復了。 – 2013-04-25 17:23:21

+0

好吧,我想我得到這是如何與x的不同範圍和lambda指向變量,謝謝本。 – BlackICE 2013-04-25 17:32:00

20

這個問題是the subject of my blog on the 20th of May 2013。感謝您的好問題!


你誤解了「詞法範圍」的含義。讓我們引用您鏈接到的文檔:

函數的主體是在定義函數時存在的舊動態環境中評估的,而不是函數調用時的當前環境。

這是你的代碼:

int x = 1; 
Func<int,int> f = y => x + y; 
x = 2; 
Console.WriteLine(f(1)); 

現在,什麼是「存在於被定義的函數時的動態環境」?把一個「環境」看作是一個階級。該類包含每個變量的可變字段。因此,這是一樣的:

Environment e = new Environment(); 
e.x = 1; 
Func<int,int> f = y => e.x + y; 
e.x = 2; 
Console.WriteLine(f(1)); 

f評估,x在f爲創建時存在的環境中即擡頭。該環境的內容發生了變化,但是f綁定的環境是相同的環境。 (請注意,這實際上是C#編譯器生成的代碼!在lambda中使用局部變量時,編譯器會生成一個特殊的「環境」類,並將本地的每種用法轉換爲字段的用法。

讓我給你舉個例子,說明如果C#是動態範圍的,那麼世界會是什麼樣子。考慮以下幾點:

class P 
{ 
    static void M() 
    { 
     int x = 1; 
     Func<int, int> f = y => x + y; 
     x = 2; 
     N(f); 
    } 
    static void N(Func<int, int> g) 
    { 
     int x = 3; 
     Console.WriteLine(g(100)); 
    } 
} 

如果C#是動態作用域那麼這將打印「103」,因爲評估g評估f,並在動態範圍的語言,評估f將查找的x值在當前環境。在當前環境中,x爲3.在創建f時存在的環境中,x爲2.同樣,該環境中的值x已更改;正如你的文件指出的那樣,環境是動態環境。但哪個環境相關不變。

現在大多數語言都不是動態範圍的,但有一些。 PostScript,例如 - 在打印機上運行的語言 - 是動態範圍的。

+0

所以你說C#閉包捕獲變量作爲它的環境而不是變量的值。是否有可能存在一個命令式語言是詞法​​範圍的,但它的封閉只能捕獲值,因此在例子中返回2? – colinfang 2013-04-26 00:05:07

+1

@colinfang是的。在新的C++中有一個關閉變量和值的語法。 – 2013-04-26 02:11:19

+0

嗯,值類型的名稱是根據值複製變量。我不明白爲什麼對於一個閉包而言,值的類型不是簡單地「被值捕獲」,就像在一個方法中一樣。同樣,對於由閉包捕獲的引用類型,使用「通過引用複製」。 – colinfang 2013-05-13 15:36:30

相關問題