青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆-341  評(píng)論-2670  文章-0  trackbacks-0

人們都很喜歡討論閉包這個(gè)概念。其實(shí)這個(gè)概念對(duì)于寫代碼來(lái)講一點(diǎn)用都沒(méi)有,寫代碼只需要掌握好lambda表達(dá)式和class+interface的語(yǔ)義就行了。基本上只有在寫編譯器和虛擬機(jī)的時(shí)候才需要管什么是閉包。不過(guò)因?yàn)橄盗形恼轮黝}的緣故,在這里我就跟大家講一下閉包是什么東西。在理解閉包之前,我們得先理解一些常見(jiàn)的argument passing和symbol resolving的規(guī)則。

首先第一個(gè)就是call by value了。這個(gè)規(guī)則我們大家都很熟悉,因?yàn)榱餍械恼Z(yǔ)言都是這么做的。大家還記得剛開(kāi)始學(xué)編程的時(shí)候,書(shū)上總是有一道題目,說(shuō)的是:

void Swap(int a, int b)
{
    int t = a;
    a = b;
    b = t;
}

int main()
{
    int a=0;
    int b=1;
    Swap(a, b);
    printf("%d, %d", a, b);
}

 

然后問(wèn)程序會(huì)輸出什么。當(dāng)然我們現(xiàn)在都知道,a和b仍然是0和1,沒(méi)有受到變化。這就是call by value。如果我們修改一下規(guī)則,讓參數(shù)總是通過(guò)引用傳遞進(jìn)來(lái),因此Swap會(huì)導(dǎo)致main函數(shù)最后會(huì)輸出1和0的話,那這個(gè)就是call by reference了。

除此之外,一個(gè)不太常見(jiàn)的例子就是call by need了。call by need這個(gè)東西在某些著名的實(shí)用的函數(shù)式語(yǔ)言(譬如Haskell)是一個(gè)重要的規(guī)則,說(shuō)的就是如果一個(gè)參數(shù)沒(méi)被用上,那傳進(jìn)去的時(shí)候就不會(huì)執(zhí)行。聽(tīng)起來(lái)好像有點(diǎn)玄,我仍然用C語(yǔ)言來(lái)舉個(gè)例子。

int Add(int a, int b)
{
    return a + b;
}

int Choose(bool first, int a, int b)
{
    return first ? a : b;
}

int main()
{
    int r = Choose(false, Add(1, 2), Add(3, 4));
    printf("%d", r);
}

 

這個(gè)程序Add會(huì)被調(diào)用多少次呢?大家都知道是兩次。但是在Haskell里面這么寫的話,就只會(huì)被調(diào)用一次。為什么呢?因?yàn)镃hoose的第一個(gè)參數(shù)是false,所以函數(shù)的返回值只依賴與b,而不依賴與a。所以在main函數(shù)里面它感覺(jué)到了這一點(diǎn),于是只算Add(3, 4),不算Add(1, 2)。不過(guò)大家別以為這是因?yàn)榫幾g器優(yōu)化的時(shí)候內(nèi)聯(lián)了這個(gè)函數(shù)才這么干的,Haskell的這個(gè)機(jī)制是在運(yùn)行時(shí)起作用的。所以如果我們寫了個(gè)快速排序的算法,然后把一個(gè)數(shù)組排序后只輸出第一個(gè)數(shù)字,那么整個(gè)程序是O(n)時(shí)間復(fù)雜度的。因?yàn)榭焖倥判虻腶verage case在把第一個(gè)元素確定下來(lái)的時(shí)候,只花了O(n)的時(shí)間。再加上整個(gè)程序只輸出第一個(gè)數(shù)字,所以后面的他就不算了,于是整個(gè)程序也是O(n)。

于是大家知道call by name、call by reference和call by need了。現(xiàn)在來(lái)給大家講一個(gè)call by name的神奇的規(guī)則。這個(gè)規(guī)則神奇到,我覺(jué)得根本沒(méi)辦法駕馭它來(lái)寫出一個(gè)正確的程序。我來(lái)舉個(gè)例子:

int Set(int a, int b, int c, int d)
{
    a += b;
    a += c;
    a += d;
}

int main()
{
    int i = 0;
    int x[3] = {1, 2, 3};
    Set(x[i++], 10, 100, 1000);
    printf("%d, %d, %d, %d", x[0], x[1], x[2], i);
}

 

學(xué)過(guò)C語(yǔ)言的都知道這個(gè)程序其實(shí)什么都沒(méi)做。如果把C語(yǔ)言的call by value改成了call by reference的話,那么x和i的值分別是{1111, 2, 3}和1。但是我們知道,人類的想象力是很豐富的,于是發(fā)明了一種叫做call by name的規(guī)則。call by name也是call by reference的,但是區(qū)別在于你每一次使用一個(gè)參數(shù)的時(shí)候,程序都會(huì)把計(jì)算這個(gè)參數(shù)的表達(dá)式執(zhí)行一遍。因此,如果把C語(yǔ)言的call by value換成call by name,那么上面的程序做的事情實(shí)際上就是:

x[i++] += 10;
x[i++] += 100;
x[i++] += 1000;

 

程序執(zhí)行完之后x和i的值就是{11, 102, 1003}和3了。

很神奇對(duì)吧,稍微不注意就會(huì)中招,是個(gè)大坑,基本沒(méi)法用對(duì)吧。那你們還整天用C語(yǔ)言的宏來(lái)代替函數(shù)干什么呢。我依稀記得Ada有網(wǎng)友指出這是Algol 60)還是什么語(yǔ)言就是用這個(gè)規(guī)則的,印象比較模糊。

講完了argument passing的事情,在理解lambda表達(dá)式之前,我們還需要知道兩個(gè)流行的symbol resolving的規(guī)則。所謂的symbol resolving講的就是解決程序在看到一個(gè)名字的時(shí)候,如何知道這個(gè)名字到底指向的是誰(shuí)的問(wèn)題。于是我又可以舉一個(gè)簡(jiǎn)單粗暴的例子了:

Action<int> SetX()
{
    int x = 0;
    return (int n)=>
    {
        x = n;
    };
}

void Main()
{
    int x = 10;
    var setX = SetX();
    setX(20);
    Console.WriteLine(x);
}

 

弱智都知道這個(gè)程序其實(shí)什么都沒(méi)做,就輸出10。這是因?yàn)镃#用的symbol resolving地方法是lexical scoping。對(duì)于SetX里面那個(gè)lambda表達(dá)式來(lái)講,那個(gè)x是SetX的x而不是Main的x,因?yàn)閘exical scoping的含義就是,在定義的地方向上查找名字。那為什么不能在運(yùn)行的時(shí)候向上查找名字從而讓SetX里面的lambda表達(dá)式實(shí)際上訪問(wèn)的是Main函數(shù)里面的x呢?其實(shí)是有人這么干的。這種做法叫dynamic scoping。我們知道,著名的javascript語(yǔ)言的eval函數(shù),字符串參數(shù)里面的所有名字就是在運(yùn)行的時(shí)候查找的。

=======================我是背景知識(shí)的分割線=======================

想必大家都覺(jué)得,如果一個(gè)語(yǔ)言的lambda表達(dá)式在定義和執(zhí)行的時(shí)候采用的是lexical scoping和call by value那該有多好呀。流行的語(yǔ)言都是這么做的。就算規(guī)定到這么細(xì),那還是有一個(gè)分歧。到底一個(gè)lambda表達(dá)式抓下來(lái)的外面的符號(hào)是只讀的還是可讀寫的呢?python告訴我們,這是只讀的。C#和javascript告訴我們,這是可讀寫的。C++告訴我們,你們自己來(lái)決定每一個(gè)符號(hào)的規(guī)則。作為一個(gè)對(duì)語(yǔ)言了解得很深刻,知道自己每一行代碼到底在做什么,而且還很有自制力的程序員來(lái)說(shuō),我還是比較喜歡C#那種做法。因?yàn)槠鋵?shí)C++就算你把一個(gè)值抓了下來(lái),大部分情況下還是不能優(yōu)化的,那何苦每個(gè)變量都要我自己說(shuō)明我到底是想只讀呢,還是要讀寫都可以呢?函數(shù)體我怎么用這個(gè)變量不是已經(jīng)很清楚的表達(dá)出來(lái)了嘛。

那說(shuō)到底閉包是什么呢?閉包其實(shí)就是那個(gè)被lambda表達(dá)式抓下來(lái)的“上下文”加上函數(shù)本身了。像上面的SetX函數(shù)里面的lambda表達(dá)式的閉包,就是x變量。一個(gè)語(yǔ)言有了帶閉包的lambda表達(dá)式,意味著什么呢?我下面給大家展示一小段代碼。現(xiàn)在要從動(dòng)態(tài)類型的的lambda表達(dá)式開(kāi)始講,就湊合著用那個(gè)無(wú)聊的javascript吧:

function pair(a, b) {
    return function(c) {
        return c(a, b);
    };
}

function first(a, b) {
    return a;
}

function second(a, b) {
    return b;
}

var p = pair(1, pair(2, 3));
var a = p(first);
var b = p(second)(first);
var c = p(second)(second);
print(a, b, c);

 

這個(gè)程序的a、b和c到底是什么值呢?當(dāng)然就算看不懂這個(gè)程序的人也可以很快猜出來(lái)他們是1、2和3了,因?yàn)樽兞棵麑?shí)在是定義的太清楚了。那么程序的運(yùn)行過(guò)程到底是怎么樣的呢?大家可以看到這個(gè)程序的任何一個(gè)值在創(chuàng)建之后都沒(méi)有被第二次賦值過(guò),于是這種程序就是沒(méi)有副作用的,那就代表其實(shí)在這里call by value和call by need是沒(méi)有區(qū)別的。call by need意味著函數(shù)的參數(shù)的求值順序也是無(wú)所謂的。在這種情況下,程序就變得跟數(shù)學(xué)公式一樣,可以推導(dǎo)了。那我們現(xiàn)在就來(lái)推導(dǎo)一下:

var p = pair(1, pair(2, 3));
var a = p(first);

// ↓↓↓↓↓

var p = function(c) {
    return c(1, pair(2, 3));
};
var a = p(first);

// ↓↓↓↓↓

var a = first(1, pair(2, 3));

// ↓↓↓↓↓

var a = 1;

 

這也算是個(gè)老掉牙的例子了啊。閉包在這里體現(xiàn)了他強(qiáng)大的作用,把參數(shù)保留了起來(lái),我們可以在這之后進(jìn)行訪問(wèn)。仿佛我們寫的就是下面這樣的代碼:

var p = {
    first : 1,
    second : {
        first : 1,
        second : 2,
    }
};

var a = p.first;
var b = p.second.first;
var c = p.second.second;

 

于是我們得到了一個(gè)結(jié)論,(帶閉包的)lambda表達(dá)式可以代替一個(gè)成員為只讀的struct了。那么,成員可以讀寫的struct要怎么做呢?做法當(dāng)然跟上面的不一樣。究其原因,就是因?yàn)閖avascript使用了call by value的規(guī)則,使得pair里面的return c(a, b);沒(méi)辦法將a和b的引用傳遞給c,這樣就沒(méi)有人可以修改a和b的值了。雖然a和b在那些c里面是改不了的,但是pair函數(shù)內(nèi)部是可以修改的。如果我們要堅(jiān)持只是用lambda表達(dá)式的話,就得要求c把修改后的所有“這個(gè)struct的成員變量”都拿出來(lái)。于是就有了下面的代碼:

// 在這里我們繼續(xù)使用上面的pair、first和second函數(shù)

function mutable_pair(a, b) {
    return function(c) {
        var x = c(a, b);
        // 這里我們把pair當(dāng)鏈表用,一個(gè)(1, 2, 3)的鏈表會(huì)被儲(chǔ)存為pair(1, pair(2, pair(3, null)))
        a = x(second)(first);
        b = x(second)(second)(first);
        return x(first);
    };
}

function get_first(a, b) {
    return pair(a, pair(a, pair(b, null)));
}

function get_second(a, b) {
    return pair(b, pair(a, pair(b, null)));
}

function set_first(value) {
    return function(a, b) {
        return pair(undefined, pair(value, pair(b, null)));
    };
}

function set_second(value) {
    return function(a, b) {
        return pair(undefined, pair(a, pair(value, null)));
    };
}

var p = mutable_pair(1, 2);
var a = p(get_first);
var b = p(get_second);
print(a, b);
p(set_first(3));
p(set_second(4));
var c = p(get_first);
var d = p(get_second);
print(c, d);

 

我們可以看到,因?yàn)間et_first和get_second做了一個(gè)只讀的事情,所以返回的鏈表的第二個(gè)值(代表新的a)和第三個(gè)值(代表新的b)都是舊的a和b。但是set_first和set_second就不一樣了。因此在執(zhí)行到第二個(gè)print的時(shí)候,我們可以看到p的兩個(gè)值已經(jīng)被更改成了3和4。

雖然這里已經(jīng)涉及到了“綁定過(guò)的變量重新賦值”的事情,不過(guò)我們還是可以嘗試推導(dǎo)一下,究竟p(set_first(3));的時(shí)候究竟干了什么事情:

var p = mutable_pair(1, 2);
p(set_first(3));

// ↓↓↓↓↓

p = return function(c) {
    var x = c(1, 2);
    a = x(second)(first);
    b = x(second)(second)(first);
    return x(first);
};
p(set_first(3));

// ↓↓↓↓↓

var x = set_first(3)(1, 2);
p.a = x(second)(first); // 這里的a和b是p的閉包內(nèi)包含的上下文的變量了,所以這么寫會(huì)清楚一點(diǎn)
p.b = x(second)(second)(first);
// return x(first);出來(lái)的值沒(méi)人要,所以省略掉。

// ↓↓↓↓↓

var x = (function(a, b) {
    return pair(undefined, pair(3, pair(b, null)));
})(1, 2);
p.a = x(second)(first);
p.b = x(second)(second)(first);

// ↓↓↓↓↓

x = pair(undefined, pair(3, pair(2, null)));
p.a = x(second)(first);
p.b = x(second)(second)(first);

// ↓↓↓↓↓

p.a = 3;
p.b = 2;

 

由于涉及到了上下文的修改,這個(gè)推導(dǎo)嚴(yán)格上來(lái)說(shuō)已經(jīng)不能叫推導(dǎo)了,只能叫解說(shuō)了。不過(guò)我們可以發(fā)現(xiàn),僅僅使用可以捕捉可讀寫的上下文的lambda表達(dá)式,已經(jīng)可以實(shí)現(xiàn)可讀寫的struct的效果了。而且這個(gè)struct的讀寫是通過(guò)getter和setter來(lái)實(shí)現(xiàn)的,于是只要我們寫的復(fù)雜一點(diǎn),我們就得到了一個(gè)interface。于是那個(gè)mutable_pair,就可以看成是一個(gè)構(gòu)造函數(shù)了。

大括號(hào)不能換行的代碼真他媽的難讀啊,遠(yuǎn)遠(yuǎn)望去就像一坨屎!go語(yǔ)言還把javascript自動(dòng)補(bǔ)全分號(hào)的算法給抄去了,真是沒(méi)品位。

所以,interface其實(shí)跟lambda表達(dá)是一樣,也可以看成是一個(gè)閉包。只是interface的入口比較多,lambda表達(dá)式的入口只有一個(gè)(類似于C++的operator())。大家可能會(huì)問(wèn),class是什么呢?class當(dāng)然是interface內(nèi)部不可告人的實(shí)現(xiàn)細(xì)節(jié)的。我們知道,依賴實(shí)現(xiàn)細(xì)節(jié)來(lái)編程是不對(duì)的,所以我們要依賴接口編程

當(dāng)然,即使是倉(cāng)促設(shè)計(jì)出javascript的那個(gè)人,大概也是知道構(gòu)造函數(shù)也是一個(gè)函數(shù)的,而且類的成員跟函數(shù)的上下文鏈表的節(jié)點(diǎn)對(duì)象其實(shí)沒(méi)什么區(qū)別。于是我們會(huì)看到,javascript里面是這么做面向?qū)ο蟮氖虑榈模?

function rectangle(a, b) {
    this.width = a;
    this.height = height;
}

rectangle.prototype.get_area = function() {
    return this.width * this.height;
};

var r = new rectangle(3, 4);
print(r.get_area());

 

然后我們就拿到了一個(gè)3×4的長(zhǎng)方形的面積12了。不過(guò)javascript給我們帶來(lái)的一點(diǎn)點(diǎn)小困惑是,函數(shù)的this參數(shù)其實(shí)是dynamic scoping的,也就是說(shuō),這個(gè)this到底是什么,要看你在哪如何調(diào)用這個(gè)函數(shù)。于是其實(shí)

obj.method(args)

 

整個(gè)東西是一個(gè)語(yǔ)法,它代表method的this參數(shù)是obj,剩下的參數(shù)是args。可惜的是,這個(gè)語(yǔ)法并不是由“obj.member”和“func(args)”組成的。那么在上面的例子中,如果我們把代碼改為:

var x = r.get_area;
print(x());

 

結(jié)果是什么呢?反正不是12。如果你在C#里面做這個(gè)事情,效果就跟javascript不一樣了。如果我們有下面的代碼:

class Rectangle
{
    public int width;
    public int height;

    public int GetArea()
    {
        return width * height;
    }
};

 

那么下面兩段代碼的意思是一樣的:

var r = new Rectangle
{
    width = 3;
    height = 4;
};

// 第一段代碼
Console.WriteLine(r.GetArea());

// 第二段代碼
Func<int> x = r.GetArea;
Console.WriteLine(x());

 

究其原因,是因?yàn)閖avascript把obj.method(a, b)解釋成了GetMember(obj, “method”).Invoke(a, b, this = r);了。所以你做r.get_area的時(shí)候,你拿到的其實(shí)是定義在rectangle.prototype里面的那個(gè)東西。但是C#做的事情不一樣,C#的第二段代碼其實(shí)相當(dāng)于:

Func<int> x = ()=>
{
    return r.GetArea();
};
Console.WriteLine(x());

 

所以說(shuō)C#這個(gè)做法比較符合直覺(jué)啊,為什么dynamic scoping(譬如javascript的this參數(shù))和call by name(譬如C語(yǔ)言的宏)看起來(lái)都那么屌絲,總是讓人掉坑里,就是因?yàn)檫`反了直覺(jué)。不過(guò)javascript那么做還是情有可原的。估計(jì)第一次設(shè)計(jì)這個(gè)東西的時(shí)候,收到了靜態(tài)類型語(yǔ)言太多的影響,于是把obj.method(args)整個(gè)當(dāng)成了一個(gè)整體來(lái)看。因?yàn)樵贑++里面,this的確就是一個(gè)參數(shù),只是她不能讓你obj.method,得寫&TObj::method,然后還有一個(gè)專門填this參數(shù)的語(yǔ)法——沒(méi)錯(cuò),就是.*和->*操作符了。

假如說(shuō),javascript的this參數(shù)要做成lexical scoping,而不是dynamic scoping,那么能不能用lambda表達(dá)式來(lái)模擬interface呢?這當(dāng)然是可以,只是如果不用prototype的話,那我們就會(huì)喪失javascript愛(ài)好者們千方百計(jì)絞盡腦汁用盡奇技淫巧鎖模擬出來(lái)的“繼承”效果了:

function mutable_pair(a, b) {
    _this = {
        get_first = function() { return a; },
        get_second = function() { return b; },
        set_first = function(value) { a = value; },
        set_second = function(value) { b = value; }
    };
return _this; } var p = new mutable_pair(1, 2); var a = p.get_first(); var b = p.get_second(); print(a, b); var c = p.set_first(3); var d = p.set_second(4); print(c, d);

 

這個(gè)時(shí)候,即使你寫

var x = p.set_first;
var y = p.set_second;
x(3);
y(4);

 

代碼也會(huì)跟我們所期望的一樣正常工作了。而且創(chuàng)造出來(lái)的r,所有的成員變量都屏蔽掉了,只留下了幾個(gè)函數(shù)給你。與此同時(shí),函數(shù)里面訪問(wèn)_this也會(huì)得到創(chuàng)建出來(lái)的那個(gè)interface了。

大家到這里大概已經(jīng)明白閉包、lambda表達(dá)式和interface之間的關(guān)系了吧。我看了一下之前寫過(guò)的六篇文章,加上今天這篇,內(nèi)容已經(jīng)覆蓋了有:

  1. 閱讀C語(yǔ)言的復(fù)雜的聲明語(yǔ)法
  2. 什么是語(yǔ)法噪音
  3. 什么是語(yǔ)法的一致性
  4. C++的const的意思
  5. C#的struct和property的問(wèn)題
  6. C++的多重繼承
  7. 封裝到底意味著什么
  8. 為什么exception要比error code寫起來(lái)干凈、容易維護(hù)而且不需要太多的溝通
  9. 為什么C#的有些interface應(yīng)該表達(dá)為concept
  10. 模板和模板元編程
  11. 協(xié)變和逆變
  12. type rich programming
  13. OO的消息發(fā)送的含義
  14. 虛函數(shù)表是如何實(shí)現(xiàn)的
  15. 什么是OO里面的類型擴(kuò)展開(kāi)放/封閉與邏輯擴(kuò)展開(kāi)放/封閉
  16. visitor模式如何逆轉(zhuǎn)類型和邏輯的擴(kuò)展和封閉
  17. CPS(continuation passing style)變換與異步調(diào)用的異常處理的關(guān)系
  18. CPS如何讓exception變成error code
  19. argument passing和symbol resolving
  20. 如何用lambda實(shí)現(xiàn)mutable struct和immutable struct
  21. 如何用lambda實(shí)現(xiàn)interface

想了想,大概通俗易懂的可以自學(xué)成才的那些東西大概都講完了。當(dāng)然,系列是不會(huì)在這里就結(jié)束的,只是后面的東西,大概就需要大家多一點(diǎn)思考了。

寫程序講究行云流水。只有自己勤于思考,勤于做實(shí)驗(yàn),勤于造輪子,才能讓編程的學(xué)習(xí)事半功倍。

posted on 2013-07-05 06:31 陳梓瀚(vczh) 閱讀(9488) 評(píng)論(12)  編輯 收藏 引用 所屬分類: 啟示

評(píng)論:
# re: 如何設(shè)計(jì)一門語(yǔ)言(七)&mdash;&mdash;閉包、lambda和interface 2013-07-05 22:58 | OpenGG
function MutablePair(a, b) {
var _this = {
get_first: function() { return a; },
get_second: function() { return b; },
set_first: function(value) { a = value; },
set_second: function(value) { b = value; }
};
return _this;
}

var p = new MutablePair(1, 2);
var q = new MutablePair(1, 2);
console.log(p.get_first === q.get_first);  回復(fù)  更多評(píng)論
  
# re: 如何設(shè)計(jì)一門語(yǔ)言(七)&mdash;&mdash;閉包、lambda和interface 2013-07-06 01:35 | 陳梓瀚(vczh)
@OpenGG
false,因?yàn)閜.get_first和q.get_first返回的是不同來(lái)源的東西,所以是兩個(gè)不同的函數(shù)。  回復(fù)  更多評(píng)論
  
# re: 如何設(shè)計(jì)一門語(yǔ)言(七)&mdash;&mdash;閉包、lambda和interface 2013-07-06 04:13 | 溪流
學(xué)習(xí)了  回復(fù)  更多評(píng)論
  
# re: 如何設(shè)計(jì)一門語(yǔ)言(七)&mdash;&mdash;閉包、lambda和interface 2013-07-07 18:30 | up
call by name不是Ada,是Alogl使用的一種方式,它出現(xiàn)的非常早。Fortran(原始的)的所有參數(shù)都是call by ref的,實(shí)際上,call by ref出現(xiàn)的比call by value早。call by value應(yīng)該是最受限的一種方式了。實(shí)際上,語(yǔ)言發(fā)展的歷史可以看作是各種設(shè)施能力不斷被限制的歷史。  回復(fù)  更多評(píng)論
  
# re: 如何設(shè)計(jì)一門語(yǔ)言(七)&mdash;&mdash;閉包、lambda和interface 2013-07-18 04:09 | rink1969
call by name 那個(gè)包袱抖的不錯(cuò)
mutable的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì),果然跟求導(dǎo)有些像  回復(fù)  更多評(píng)論
  
# re: 如何設(shè)計(jì)一門語(yǔ)言(七)&mdash;&mdash;閉包、lambda和interface 2013-07-24 00:08 | Scan
老大為什么不用標(biāo)記來(lái)區(qū)分lambda的環(huán)境accessor的用途呢?我覺(jué)得這樣簡(jiǎn)單些啊

function pair(a, b)
return function(accessor, isSetter)
if isSetter then a, b = accessor(a, b)
else return accessor(a, b) end
end
end
function get_first(pair)
return pair(function(a, b) return a end)
end
function get_second(pair)
return pair(function(a, b) return b end)
end
function set_first(pair, v)
pair(function(a, b) return v, b end, true)
end
function set_second(pair, v)
pair(function(a, b) return a, v end, true)
end

local p = pair(1, 2)
print(get_first(p))
print(get_second(p))
set_first(p, 3)
print(get_first(p))
print(get_second(p))
  回復(fù)  更多評(píng)論
  
# re: 如何設(shè)計(jì)一門語(yǔ)言(七)&mdash;&mdash;閉包、lambda和interface[未登錄](méi) 2013-07-24 00:56 | 陳梓瀚(vczh)
@Scan
拐彎抹角啊  回復(fù)  更多評(píng)論
  
# re: 如何設(shè)計(jì)一門語(yǔ)言(七)&mdash;&mdash;閉包、lambda和interface 2013-10-24 06:38 | SLiang
我試了一下最后那段javascript代碼,似乎他返回的結(jié)果感覺(jué)用的是lexical scoping

function mutable_pair(a, b) {
_this = {
get_first : function() { return a; },
get_second : function() { return b; },
set_first : function(value) { a = value; },
set_second : function(value) { b = value; }
};
return _this;
}

var p = new mutable_pair(1, 2);
var x = p.set_first
x(4)
var k = p.get_first();

然后k的結(jié)果是4。  回復(fù)  更多評(píng)論
  
# re: 如何設(shè)計(jì)一門語(yǔ)言(七)&mdash;&mdash;閉包、lambda和interface 2013-10-24 06:59 | SLiang
理解錯(cuò)你的話了,請(qǐng)忽略我上一個(gè)問(wèn)題。

我真正的問(wèn)題是,如果用你最后一段js那種閉包寫法,如何實(shí)現(xiàn)繼承的效果呢?還有最后那個(gè)
var p = new mutable_pair(1, 2);
去掉new似乎也是一樣的效果  回復(fù)  更多評(píng)論
  
# re: 如何設(shè)計(jì)一門語(yǔ)言(七)&mdash;&mdash;閉包、lambda和interface 2013-10-24 22:59 | 陳梓瀚(vczh)
@SLiang
js的繼承怎么寫現(xiàn)在大家都有相當(dāng)?shù)挠懻摿耍闳ニ岩凰褢?yīng)該會(huì)比我告訴你的更全面。你還可以看看TypeScript,看他是怎么生成繼承的js代碼的。  回復(fù)  更多評(píng)論
  
# re: 如何設(shè)計(jì)一門語(yǔ)言(七)&mdash;&mdash;閉包、lambda和interface[未登錄](méi) 2013-11-10 03:51 | patz
> 大家到這里大概已經(jīng)明白閉包、lambda表達(dá)式和interface之間的關(guān)系了吧。

我艸這句話是怎么突然蹦出來(lái)的,感覺(jué)就像汪峰求愛(ài)結(jié)果大家都去看亞冠新聞沒(méi)人理……

結(jié)論導(dǎo)出的太快了,差評(píng)!還好看過(guò)SICP,不然真想不清楚。  回復(fù)  更多評(píng)論
  
# re: 如何設(shè)計(jì)一門語(yǔ)言(七)&mdash;&mdash;閉包、lambda和interface 2014-03-04 03:44 | nic
call by need 是求值順序,怎么和傳參混在一塊說(shuō)  回復(fù)  更多評(píng)論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲视频欧美在线| 国产精品久久久久久久久久妞妞 | 亚洲免费网站| 国产精品高精视频免费| 亚洲图片激情小说| 一本久道久久久| 国产精品xxxxx| 亚洲欧美日本在线| 亚洲精品在线观看视频| 欧美日韩精品三区| 亚洲网站啪啪| 国产精品99久久久久久久女警| 欧美日韩视频在线一区二区| 亚洲视屏在线播放| 亚洲一区二区不卡免费| 欧美性大战久久久久久久| 午夜精品福利在线观看| 午夜精品久久久久99热蜜桃导演| 国产偷国产偷精品高清尤物| 久久久综合香蕉尹人综合网| 久久免费高清| 亚洲美女性视频| 夜夜嗨av一区二区三区网页| 国产精品手机在线| 久久久噜久噜久久综合| 久久女同互慰一区二区三区| 91久久精品国产91性色| 亚洲免费电影在线| 国产精品欧美经典| 久久在线播放| 免费一级欧美片在线播放| av不卡免费看| 亚洲免费伊人电影在线观看av| 国产亚洲人成a一在线v站| 欧美 日韩 国产在线 | 蜜臀av国产精品久久久久| 亚洲久久成人| 亚洲图片欧美午夜| 黄网动漫久久久| 亚洲国产精品va在看黑人| 久久一区二区三区超碰国产精品| 日韩一二三在线视频播| 亚洲一二三区精品| 一区二区视频免费完整版观看| 亚洲国产成人在线视频| 欧美性一区二区| 久久蜜桃资源一区二区老牛 | 亚洲国产日韩在线一区模特| 亚洲日韩第九十九页| 国产欧美一区二区色老头| 欧美华人在线视频| 国产精品成人一区二区| 老司机67194精品线观看| 欧美精品在线观看| 久久精品国产欧美亚洲人人爽| 美日韩精品视频| 亚洲欧美日韩国产精品| 久久亚洲国产精品一区二区| 日韩一级在线观看| 亚洲影音一区| 亚洲精品色图| 性欧美18~19sex高清播放| 亚洲精品少妇| 欧美在线免费看| 在线视频亚洲| 久久久欧美精品| 亚洲男人第一av网站| 美女国内精品自产拍在线播放| 亚洲欧美国产日韩天堂区| 久久久欧美一区二区| 中文日韩在线视频| 久久久综合网| 香蕉久久精品日日躁夜夜躁| 欧美电影在线观看完整版| 久久国产精品亚洲va麻豆| 欧美日本国产一区| 久久婷婷人人澡人人喊人人爽| 欧美午夜欧美| 亚洲福利视频一区二区| 国产亚洲一区二区精品| 一区二区日韩免费看| 亚洲国产日韩美| 欧美一区二区三区四区在线观看| 99精品国产99久久久久久福利| 久久九九热re6这里有精品| 亚洲一区二区免费视频| 欧美福利视频一区| 久久久成人精品| 欧美三级乱人伦电影| 欧美顶级大胆免费视频| 国产日韩在线看片| 一区二区精品在线| 99riav1国产精品视频| 久久精品一区二区三区不卡| 亚洲综合999| 欧美高清视频一区二区三区在线观看 | 久久久欧美一区二区| 国产精品成人一区二区三区夜夜夜 | 国模私拍一区二区三区| 中文一区二区在线观看| 日韩一级在线观看| 欧美成人一区二区在线| 蜜臀99久久精品久久久久久软件| 国产日韩欧美黄色| 亚洲一区精彩视频| 亚洲自拍偷拍色片视频| 欧美精品在线极品| 亚洲国产一区二区a毛片| 亚洲高清在线视频| 久久国产精品毛片| 久久不射2019中文字幕| 国产精品天美传媒入口| 一本色道久久加勒比88综合| 一本在线高清不卡dvd| 欧美精品电影| 欧美激情亚洲精品| 亚洲国产老妈| 麻豆国产精品一区二区三区 | 99riav久久精品riav| 欧美成人网在线| 亚洲国产成人91精品| 最新精品在线| 欧美风情在线| 亚洲黄网站在线观看| 亚洲激情第一区| 模特精品在线| 亚洲黄页视频免费观看| 亚洲精品中文在线| 欧美日韩1080p| 亚洲免费观看视频| 99亚洲一区二区| 欧美日一区二区三区在线观看国产免| 亚洲美女少妇无套啪啪呻吟| 一区二区三区欧美亚洲| 欧美四级伦理在线| 亚洲午夜高清视频| 欧美一区激情视频在线观看| 国产日韩欧美夫妻视频在线观看| 一区二区三区日韩精品视频| 亚洲一区二区三区四区五区午夜| 国产精品红桃| 亚洲欧美一区二区三区久久| 久久久久99| 亚洲盗摄视频| 久久综合五月| 亚洲成人自拍视频| 一区二区三区高清在线| 国产精品久久久久9999吃药| 午夜免费在线观看精品视频| 久久精品国产69国产精品亚洲| 黄色精品在线看| 麻豆精品精品国产自在97香蕉| 亚洲激情在线观看视频免费| 国产精品99久久99久久久二8| 国产精品久久久久久av福利软件| 亚洲欧美中文日韩在线| 久久亚洲综合| 亚洲国产精品一区二区第四页av| 欧美伦理91i| 亚洲午夜精品17c| 久久精品主播| 91久久久久久久久久久久久| 欧美日韩一区在线视频| 午夜精品亚洲| 欧美黄色一级视频| 亚洲欧美日韩精品综合在线观看| 国产一区二区久久久| 欧美a级片网| 亚洲少妇最新在线视频| 久久午夜精品| 99精品国产在热久久婷婷| 国产欧美日韩综合精品二区| 可以看av的网站久久看| 夜夜爽99久久国产综合精品女不卡| 久久精品国产清自在天天线| 亚洲精品黄色| 国产精品乱码久久久久久| 久久日韩精品| 亚洲视频网站在线观看| 免费影视亚洲| 亚洲综合国产| 国语自产精品视频在线看一大j8| 欧美激情一区二区三区四区| 亚洲欧美日韩一区二区在线| 嫩草影视亚洲| 亚洲欧美日韩国产一区| 亚洲国产欧美日韩另类综合| 欧美性色综合| 久久在线视频在线| 亚洲图片欧美日产| 亚洲电影激情视频网站| 羞羞答答国产精品www一本| 亚洲福利在线看| 国产乱子伦一区二区三区国色天香| 免费在线视频一区| 欧美一区二区视频在线观看| 亚洲精品一区二区网址| 久久久夜精品| 亚洲女同同性videoxma| 亚洲精品九九|