數學上的閉包概念及與編程的關係

 

首先, 須要強調一點, 這裏談論的 閉包(closure) 概念是指數學上的, 不是咱們編程界通常談論的那個閉包.java

在編程實踐中, 閉包另有定義, 是一種爲表示帶有自由變量的過程而用的實現技術.程序員

但另外一方面, 這個數學上的閉包概念在編程實踐中依然是有體現, 雖然不一樣於編程界通常談論的那個閉包, 後面會舉一些例子加以說明.編程

閉包究竟是什麼?

閉包在數學上是一個比較抽象的概念, 來自於抽象代數, 所以這裏不打算直接給出它的定義, 不然你們看了估計仍是一頭霧水, 爲便於理解, 仍是先從具體的例子出發, 最後纔給出它的定義.數組

以加法在天然數集合中爲例

咱們先考察一個很簡單的例子, 就是加法在天然數集合中的操做及其結果.數據結構

首先, 天然數集這個很好理解, 就是0, 1, 2, 3..., 這些整數的集合, 固然須要注意的一點是它是一個無窮的集合.閉包

而後是加法這個操做, 咱們也很熟悉, 它須要兩個操做數, 從剛纔的天然數集合中任意取出兩個數, 而後執行加法操做:編程語言

好比, 1 + 2, 3 + 5, 6 + 4 等等ide

而後這些加法操做會有一個結果, 好比 1 + 2 = 3, 3 + 5 = 8, 6 + 4 = 10 等等, 當咱們觀察這些結果, 好比 3, 8, 10 等等時, 不難發現它們也仍是屬於天然數集合.設計

說到這裏, 你可能會想, 這不是顯而易見的嘛, 感受像是說了一堆廢話! 先別急着下定論.對象

至此, 咱們就能夠初步給出閉包的定義, 首先有一個集合(天然數集合), 而後有一個操做(加法操做), 這個操做須要集合中的兩個元素, 最後操做的結果仍然屬於這個集合.

由於結果仍然屬於這個集合, 咱們就說, 天然數集合對於加法操做來講是封閉的(closed).

這個是針對結果來講的, 也就是不管你怎麼操做來操做去, 結果都還在集合內, 有點孫悟空怎麼翻筋斗也逃不出如來佛手掌心那種感受.

天然數集合在加法這樣一種操做下是封閉的, 咱們就說它知足(satisfy)一種閉包性質(clousre property).

更通常化的說, 一個集合被認爲是知足一種閉包性質, 若是它在一個(或一系列)操做下是封閉的.

A set that is closed under an operation or collection of operations is said to satisfy a closure property.

另外一個操做: 乘法

上面的定義涉及到一個或一系列操做, 下面就說說另外一個操做, 好比乘法, 一樣仍是天然數集合, 隨便取兩個數, 而後乘起來:

好比, 2 × 3, 3 × 7, 5 × 4 等等.

而後也會有一個結果, 好比 2 × 3 = 6, 3 × 7 = 21, 5 × 4 = 20 等等, 當咱們觀察這些結果, 好比 6, 21, 20 等等時, 不難發現它們也仍是屬於天然數集合.

所以, 根據前面的定義, 咱們能夠說, 天然數集合對於乘法操做來講也是封閉的(closed).

畢竟來講, 乘法在某種意識上講也是一種加法, 所以天然數集合對其封閉也就不難理解了.

天然數集合對於加法操做來講是封閉的(closed), 同時對於乘法操做來講也是封閉的(closed), 所以, 能夠說它在一系列操做(加法及乘法)下都是封閉的.

也便是一個集合能夠不只對一個操做是封閉的, 它還可能對不少操做都是封閉的.

當減法引入時

說完了加法和乘法, 估計有些同窗已經不耐煩了, 說, 這有啥稀奇的呢? 難道有什麼操做是不封閉的嗎? 下面就來講說再另外一個操做, 減法, 而後咱們會發現, 事情會有一些變化.

若是草率地去看, 仍是針對天然數集合來講, 隨便取兩數, 而後執行減法操做:

好比, 3 - 2, 7 - 4, 5 - 1 等等;

而後也會有一個結果, 好比 3 - 2 = 1, 7 - 4 = 3, 5 - 1 = 4 等等, 當咱們觀察這些結果, 好比 1, 3, 4 等等時, 不難發現它們也仍是屬於天然數集合.

從以上來看, 咱們彷佛也能夠說, 天然數集合對於減法操做來講一樣也是封閉的(closed).

但只要咱們多考察一些情形, 好比 2 - 4, 3 - 6 等等, 就會發現, 不對勁了, 2 - 4 = -2, 3 - 6 = -3, 像 -2, -3 這些結果它們並不屬於天然數集合!

在古代, 人們的認知能力還很弱時, 面對像 2 - 4 這麼騷氣的操做時, 乾脆就像鴕鳥把頭埋進沙子裏同樣, 對其視而不見了, 人們認爲這樣的操做是沒有意義的!

因而, 天然數集合對於減法操做來講並非封閉的. 但若是咱們擴大天然數集合, 變成整數集合, 它包含了負整數集合, 0 和 正整數集合.

這時候, 對於一個整數集合而言, 則不管你怎麼去取數進行減法操做, 結果老是還在這個整數集合中.

又一次的, 孫悟空怎麼翻筋斗也逃不出如來佛手掌心, 這是個更大的手掌心!

因而, 雖然天然數集合在減法的操做下不是封閉的, 但一旦擴大到整數集合, 咱們又能夠說, 整數集合對於減法操做是封閉的.

另外一種情形則是, 對一個有限的集合而言, 好比只有一個元素 0 的集合, 表示爲 { 0 }, 咱們也照樣能夠說, 它對於加法, 乘法和減法都是封閉的, 由於 0 + 0 = 0, 0 × 0 = 0, 0 - 0 = 0, 結果不管怎麼搞都仍是 0, 所以也是封閉的. 這個例子中沒有擴大集合的範圍, 甚至是相反, 縮小了集合的範圍, 也能得出封閉的性質, 固然這就屬於比較特殊的狀況了.

閉包的另外一種定義

前面提到的一個閉包的定義是指集合知足的一種閉包性質, 對於閉包還有另一種定義.

當一個集合 S 在某些操做下不是封閉的,

好比天然數集合對於減法操做不是封閉的.

咱們能夠找到一個包含 S 的最小集合使得操做是封閉的,

好比, 能夠找到 整數集合 這個包含 天然數集合 的最小集合, 它對於減法操做是封閉的.

那麼, 這個最小的集合就叫作 S 集合(針對那些操做而言)的閉包(closure).

整數集合就是天然數集合(針對減法操做而言)的閉包.

具體的英文定義以下:

When a set S is not closed under some operations, one can usually find the smallest set containing S that is closed. This smallest closed set is called the closure of S (with respect to these operations)

這個定義跟閉包性質的定義有差別, 但二者仍是有緊密關係的, 只不過是說法的側重點不一樣, 但都是圍繞着操做結果的封閉性去說的.

當操做繼續增長

整數集合對於加法, 乘法和減法都是封閉的, 但若是繼續引入其它操做, 好比除法, 那麼新的問題又會來了, 雖然像 6 ÷ 2, 10 ÷ 5 等整除的情形, 結果還在集合中, 但更多的諸如 2 ÷ 3, 5 ÷ 8 結果就不在集合裏面了.

此時若是想讓除法操做封閉, 還得繼續擴展集合到好比有理數集合.

此外還得施加一個限制, 除數還不能爲 0.

而對於有理數集合, 當繼續擴大操做時, 好比引入了開方運算, 不少結果又不是封閉的了, 這時得擴大到無理數集合; 而即使是到了無理數集合, 對於負數的開平方依然是一籌莫展的, 這時就須要進一步的擴大到複數集合了...

顯然的, 當引入愈來愈多的操做, 想繼續的保持封閉性就很困難了.

在編程中的應用

至此, 咱們已經說完了數學上的閉包這一律念, 天然也只是走馬觀花地說一下, 讓你們有個概念而已, 更深的我們也不懂, 也不打算去涉及, 畢竟太艱深了.

經過以上的介紹, 相信你們對數學上的閉包這一律念多少也有了一些瞭解, 但不少人可能會問, 去知道這些幹啥呢? 對咱們編程彷佛也沒啥用. 而前面一開始也說了, 這裏談的閉包還不是編程界一般談論的那個閉包, 那這裏談論的這個數學上的閉包對於咱們的編程語言到底有什麼影響呢?

怎麼說好呢, 要說沒有影響恐怕是不可能的, 但若是說有多少大的影響恐怕不少人又不認同了, 問題就在於這些影響每每是很是基礎性的, 以致於咱們不以爲這算什麼影響.

舉個很是簡單的例子, 咱們在編程中極可能隨手就寫出了這樣的表達式:

2 + 4 + 5

而從不去問到底爲何, 甚至說這爲何是可能的, 咱們以爲這些彷佛是天經地義, 不值一提的.

但從閉包的角度去看, 2 + 4 + 5 之因此成爲可能, 是由於 2 + 4 的結果仍然屬於集合以內, 才所以能夠繼續參與下一次的操做.

當咱們例舉另外的例子, 好比另外一個表達式:

(3 > 2) + 1

狀況就有點微妙了. 對於有些編程語言而言, 這樣的操做仍然是可行的, 但實際上是由於包含了一種隱式的轉換; 而對於另外的一些編程語言來講, 這可能就是語法錯誤了.

從閉包的角度去看, 3 > 2 這個操做的結果並不屬於數的集合, 而是一個只有兩個有限元素的 boolean 集合: {true, false}.

由於這個操做的結果並非一個數, 因此後續的操做其實就沒有意義了.

好比 true + 1, 或者是 false + 1 都是沒有意義的了.

有些編程語言之因此可以繼續操做下去, 是由於內部作了隱式轉換, 好比把 true 轉爲 1, false 變爲 0, 因此繼續執行的實際上是 1 + 1 或者 0 + 1.

有些語言限制會比較嚴格, 好比 java, 你寫這樣的語句是要報錯的:

if (age = 60) {
    // 能夠退休了
}

這裏的一個陷阱就是, 單個的等號是一個賦值的操做, 而不是比較, 比較是用兩個等號 == 這樣:

if (age == 60) {
    // 能夠退休了
}

這樣寫的 java 程序才能編譯經過, 但有的語言兩種寫法都能編譯, 但第一種狀況實際會變成:

if (60) {
    // 能夠退休了
}

但 if 其實要求的是一個 boolean 集合的操做數, 而此刻會執行一次轉換, 通常而言, 0 會轉成 false, 其它則是 true, 因此 60 就轉換爲 true, 最後語句實質變成了:

if (true) {
    // 能夠退休了
}

這樣一來, 整個判斷就徹底多餘了, 結果老是爲真, 裏面的語句始終都能執行, 在不少狀況下, 這就是一個程序的 bug 了.

可見, 不嚴格的遵循閉包性質可能會給咱們帶來麻煩; 另外一方面, 設計良好的閉包性質有可能帶給咱們便利.

好比說, 有的語言容許數組的元素還能夠是數組, 這就給不少的操做帶來了不少方便, 這實際上是閉包性質的一個體現.

而有的語言則不容許這樣, 數組的元素只能是其它元素, 但不能是數組, 這就削弱了語言的表達能力, 進而在編程中給咱們帶來不便.

又好比, 對於不少語言來講, 對象的屬性能夠繼續是對象, 又或者說, map 的元素還能夠是另外一個 map, 這樣一來, 咱們就能夠嵌套地表達不少複雜的數據結構, 而對它們的操做又能夠比較簡化, 由於操做的結果還在集合內, 就能夠反覆運用同一種操做去操做它.

舉個例子來講, 一顆樹的節點能夠是一個葉子節點, 還還能夠是一棵子樹, 對於文件夾及裏面的文件及(或)文件夾而言就是這麼一種情形, 所以能夠用同一個操做遞歸地遍歷全部的節點.

而上述這些, 往深了說, 其實都是都是閉包性質的體現, 由於這些性質的存在, 才使得這些成爲可能.

對於咱們的編程實踐來講, 某種組合數據對象的操做知足閉包性質, 那就是說,經過它組合起數據對象獲得的結果自己還能夠經過一樣的操做再進行組合.

閉包性質是任何一種組合功能的威力的關鍵要素, 由於它使咱們可以創建起層次性的結構, 這種結構由一些部分構成, 而其中的各個部分又是由它們的部分構成, 而且能夠如此繼續下去.

你也許以爲這些是天經地義, 理所固然的, 緣由也許在於它過於本質, 過於基礎了, 以至於你都沒有覺察到它, 但它仍是在那裏發揮着它的做用.

我以爲深刻的理解這些概念是有助於咱們成爲一個更好的程序員的, 這也是這篇文章介紹這一律唸的緣由, 另外的一個緣由則是, 大量的介紹閉包概念的文章講的都是編程上的閉包, 而不是數學上的閉包.

固然這點對於咱們程序員而言也是無可厚非的, 畢竟咱們更關注編程相關的, 只是我以爲, 既然這個概念有多重的解析, 咱們多瞭解一些也不無壞處.

固然因爲我也不是什麼深刻研究數學的, 這也僅是一篇介紹性的文章, 免不了掛一漏萬, 若是你有什麼意見或建議, 歡迎留言, 關於數學上的閉包概念及其在編程中的影響就介紹到此.

相關文章
相關標籤/搜索