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#具有詞法範圍,但爲什麼這個例子顯示動態範圍行爲?
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#具有詞法範圍,但爲什麼這個例子顯示動態範圍行爲?
有關於詞彙範圍是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()));
拉姆達結合存在內部DefineIt
的x
可變。定義點的值(x = 1
)無關緊要。 變量稍後設置爲x = 3
。
但是顯然不是動態範圍要麼是因爲x = 2
裏面沒有使用InvokeIt
。
這個問題是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,例如 - 在打印機上運行的語言 - 是動態範圍的。
您在調用'f'之前更改了'x'的值。你爲什麼不期望使用'x'的當前值? – 2013-04-25 17:07:27
爲什麼4?也許你認爲2? – 2013-04-25 17:08:00
爲什麼4?我看你怎麼會認爲f(1)= 2,但是你會得到4嗎? – jure 2013-04-25 17:08:34