2010-05-19 30 views
2

我已經關閉看了不少文章,並embarassingly夠了,我還是不明白這個概念!文章解釋瞭如何用幾個例子來創建一個閉包,但我沒有看到任何關注它們的點,因爲它們在很大程度上是看起來很人造的例子。我並不是說所有這些都是人爲的,只是我發現的那些東西看起來很有意思,而且我甚至在理解了它們之後也會看到它們,我將能夠使用它們。所以爲了理解閉包,我正在看一些真正的問題,可以很自然地使用閉包來解決。自然問題要解決使用閉包

例如,一種自然的方式來解釋遞歸一個人可以解釋n的計算!理解像使用遞歸計算數字的階乘這樣的問題是很自然的。同樣,通過讀取每個元素並與所討論的數字進行比較,在未排序的數組中找到一個元素幾乎不成問題。另外,在不同層面上,面向對象編程也是有意義的。

所以我試圖找到一些可有或沒有封閉來解決問題,但使用倒閉使得想着他們,並解決這些問題更容易。此外,閉包有兩種類型,每次對閉包的調用都可以創建環境變量的副本,或引用相同的變量。那麼在哪些閉包實現中可以更自然地解決哪些問題呢?

回答

1

好了,說你的,比方說,JavaScript的生成菜單。在一個循環

var menuItems = [ 
    { id: 1, text: 'Home' }, 
    { id: 2, text: 'About' }, 
    { id: 3, text: 'Contact' } 
]; 

和你創建它們,就像這樣:

for(var i = 0; i < menuItems.length; i++) { 

    var menuItem = menuItems[i]; 

    var a = document.createElement('a'); 
    a.href = '#'; 
    a.innerHTML = menuItem.text; 

    // assign onclick listener 

    myMenu.appendChild(a); 

} 

現在,對於聽者的onclick,你可能會嘗試這樣的事情:

a.addEventListener('click', function() { 
    alert(menuItem.id); 
}, false); 

但你會發現這將有每個鏈接提醒'3'。因爲在點擊的時候,你的onclick代碼被執行,並且menuItem的值被評估到最後一個項目,因爲那是它最後一次分配的值,在for循環的最後一次迭代中。

相反,你可以做的是創建一個新的閉包的每一個環節,採用menuItem的價值,因爲它是在執行這一點上

a.addEventListener('click', (function(item) { 
    return function() { 
     alert(item.id); 
    } 
})(menuItem), false); 

所以這是怎麼回事呢?我們實際上正在創建一個函數,接受item,然後立即調用該函數,傳遞menuItem。因此,'包裝'功能不是當您點擊鏈接時將執行的功能。我們立即執行它,並將函數返回爲函數作爲點擊處理函數。

這裏發生的是,對於每次迭代,函數被調用一個新的值menuItem,並返回一個函數返回該值,該函數有效地創建一個新的閉包。

。希望茅塞頓開=)

+0

實際上相當多! – umar 2010-05-19 16:26:12

1

在C#,一個功能可以通過接近實現濾波的概念到一個點:

IEnumerable<Point> WithinRadius(this IEnumerable<Point> points, Point c, double r) 
{ 
    return points.Where(p => (p - c).Length <= r); 
} 

拉姆達(閉合)捕獲所提供的參數並結合起來到將在以後執行的計算時間。

+0

我的觀點是,爲什麼我應該嘗試使用閉包來做到這一點?爲什麼我不想在那裏執行計算?推遲計算可以獲得什麼優勢? – umar 2010-05-19 13:45:53

+0

如果'IEnumerable '是一個無限序列呢? – 2010-05-19 13:56:27

+1

@ m.u.sheikh:我不瞭解你,但是我會用閉包來做到這一點,因爲它會導致代碼少得多(並且更重要的是代碼重複性更少)。 – sepp2k 2010-05-19 14:15:48

1

好吧,我用瓶蓋每天在函數合成運算和咖喱運營商的形式,兩者都在計劃通過關閉來實現:

快速排序方案,例如:

(define (qsort lst cmp) 
    (if (null? lst) lst 
     (let* ((pivot (car lst)) 
      (stacks (split-by (cdr lst) (curry cmp pivot)))) 
     (append (qsort (cadr stacks) cmp) 
       (cons pivot 
         (qsort (car stacks) cmp)))))) 

哪裏CMP是一個二元函數通常應用於如(CMP一二),我咖喱它在這種情況下,通過創建一個單一的斷言,如果你喜歡一分爲二我的籌碼,我的咖喱運營商:

(define (curry f . args) 
    (lambda lst (apply f (append args lst)))) 

(define (curryl f . args) 
    (lambda lst (apply f (append lst args)))) 

分別右撇子和左撇子。

所以鑽營是封閉件的一個很好的例子,一個例子是功能性組合物。或者,例如一個函數,該函數使用列表並從該謂詞開始測試它的參數是否爲該列表的成員,這也是一個閉包。

3

回調是一個很好的例子。讓我們採取JavaScript。

假設你有一個新聞網站,具有新聞標題和導語短和「更多...」按鈕,每一個接一個。當用戶點擊按鈕時,您想要異步加載與點擊的按鈕對應的內容,並向用戶顯示請求標題旁邊的指示器,以便用戶可以「看到在其中工作的頁面」。

function handleClickOnReadMore(element) { 
    // the user clicked on one of our 17 "request more info" buttons. 
    // we'll put a whirly gif next to the clicked one so the user knows 
    // what he's waiting for... 
    spinner = makeSpinnerNextTo(element); 

    // now get the data from the server 
    performAJAXRequest('http://example.com/', 
     function(response) { 
     handleResponse(response); 

     // this is where the closure comes in 
     // the identity of the spinner did not 
     // go through the response, but the handler 
     // still knows it, even seconds later, 
     // because the closure remembers it. 
     stopSpinner(spinner); 
     } 
); 
} 
2

個人而言,我討厭寫排序例程當我有對象的列表。
通常你有排序功能和一個單獨的函數來比較兩個對象。
現在你可以做到這一點在一個聲明中

List<Person> fList = new List<Person>(); 
fList.Sort((a, b) => a.Age.CompareTo(b.Age)); 
+0

這是一個很好的例子。試圖「歸化」它,我發現排序例程並不知道如何事先比較列表元素,並將它傳遞給比較函數是一種很好的自然使用閉包 – umar 2010-05-19 16:10:27

+0

這是排序,但您也可以添加其他集合操作像select,filter,map(collect),reduce(fold)和each(foreach)。 – 2010-05-20 20:20:11

+1

我不同意這是一個很好的例子,因爲它沒有真正展示閉包概念的力量。原因是,它沒有顯示閉包是如何捕獲本地環境的,因爲這裏的比較器除了參數外沒有使用任何變量,這是閉包的定義特徵。它所表現的是一個匿名函數 - 順便說一句,它在大多數語言中都是閉包,但並不一定如此(匿名函數不是閉包是可能的,只是明顯不太有用)。 – Amadan 2010-05-23 15:50:51

2

好了,一段時間以來,用Scala花後,我無法想象一個代碼,而無需關閉一些名單上運行。例如:

val multiplier = (i:Int) => i * 2 
val list1 = List(1, 2, 3, 4, 5) map multiplier 

val divider = (i:Int) => i/2 
val list2 = List(1, 2, 3, 4, 5) map divider 

val map = list1 zip list2 

println(map) 

輸出將

List((2,0), (4,1), (6,1), (8,2), (10,2)) 

我不知道,如果是的例子中,你正在尋找,但我個人認爲,中倒閉的真正的力量是最好的例子可以例如:各種分類,搜索,迭代等。

+0

對不起,但這只是一個匿名函數/ HOF的例子,因爲沒有引用超出範圍的變量。 – 2013-02-20 00:48:17

2

我個人發現斯圖爾特朗格里奇在Closures in JavaScript(幻燈片pdf)大量有用的演示文稿。它充滿了很好的例子和一點幽默感。

alt text