可計(jì)算函數(shù)如何計(jì)算矩陣乘法

因?yàn)榍岸螘r(shí)間研究了很久的圖形學(xué)灸促,也寫(xiě)了很多向量和矩陣的運(yùn)算函數(shù),但是其中一些程序的編寫(xiě)和設(shè)計(jì)難度無(wú)疑讓我很難受后控,由于之前看了alan kay寫(xiě)的關(guān)于smalltalk80的歷史侣背,我突然意識(shí)到使用lambda來(lái)計(jì)算矩陣,或許會(huì)有更簡(jiǎn)單的方法堤瘤,相比于alan kay的一切都是對(duì)象的思想,它達(dá)到了無(wú)限的伸縮性浆熔,對(duì)數(shù)字和數(shù)據(jù)結(jié)構(gòu)都達(dá)到了很好的建模本辐,但是奇怪的一點(diǎn)是,使用面向?qū)ο笳Z(yǔ)言解決基礎(chǔ)算法,好像并沒(méi)有實(shí)質(zhì)性的進(jìn)步慎皱,我們先來(lái)看一個(gè)c程序:

#include <stdio.h>
#include <string.h>
int bank(char * msg, int val){
  static balance = 0;
  if(strcmp(msg,"set") == 0)
    balance = val;
  else if(strcmp(msg,"do") == 0)
    balance += val;
  else if(strcmp(msg,"get") == 0)
    return balance;
}
int main(){
  bank("set",10);
  bank("do",10);
  printf("%d",bank("get",0));
  return 0;
}

這個(gè)c程序封裝了一個(gè)static變量balance老虫,外部環(huán)境可以通過(guò) char * msg 來(lái)訪問(wèn)它,但是我們的程序還是基于變量這一個(gè)前提,無(wú)論是在smalltalk內(nèi)部茫多,還是java祈匙,都保留了內(nèi)部狀態(tài),這樣的程序?qū)嶋H上只帶來(lái)了非破壞性地梨,也就是把程序使用通信的方式組織在一起菊卷,拿到消息的每個(gè)部分只需要對(duì)接口編程缔恳,而不需要對(duì)實(shí)現(xiàn)編程宝剖,提高了程序的利用率,降低了總體代碼變化的部分歉甚,從而降低了實(shí)現(xiàn)功能的編寫(xiě)難度万细,并沒(méi)有真正從算法的角度去改變程序的內(nèi)部結(jié)構(gòu),在面對(duì)一個(gè)復(fù)雜的大型程序帶來(lái)的復(fù)雜性纸泄,這樣的方法論是很好的赖钞,但是實(shí)際上,我們還可以更進(jìn)一步思考聘裁,我們的程序?qū)嶋H上并不是在通信雪营,取而代之的是,它們是在計(jì)算衡便。
有人說(shuō)献起,這不是廢話嗎?但是仔細(xì)想想镣陕,我們的用戶使用某個(gè)軟件谴餐,到底是在干嘛,他們是在通信嗎呆抑,還是說(shuō)他們?cè)谟?jì)算岂嗓?其實(shí)可以推而廣之,我們?nèi)祟惈@取知識(shí)鹊碍,改造現(xiàn)實(shí)世界厌殉,計(jì)算更普遍,還是通信更普遍侈咕,可能不同的人有不同的結(jié)論公罕,但是兩者在某些時(shí)候是等同的,比如說(shuō)乎完,一個(gè)正在建造航天飛機(jī)的工程師熏兄,他急需解決一個(gè)空氣動(dòng)力學(xué)方程從而設(shè)計(jì)機(jī)翼的寬度,他有可能會(huì)選擇自己計(jì)算出來(lái),他也可以找某個(gè)團(tuán)隊(duì)里的數(shù)學(xué)很好的助手摩桶,計(jì)算出來(lái)桥状,他只要知道結(jié)果就好了,然而這個(gè)助手的可信度硝清,或許比起計(jì)算機(jī)辅斟,會(huì)有一定的差異,他有可能更相信某個(gè)科學(xué)計(jì)算軟件芦拿,也可能正好相反士飒,但是可以知道的是,他自己計(jì)算出來(lái)蔗崎,和借助某些外部力量知道了這個(gè)方程的解酵幕,只取決于他的信任度,答案只是一個(gè)比特位描述的符號(hào)或者方程式缓苛,他只需要關(guān)注它的可信度芳撒,到底是計(jì)算得來(lái),還是某種通信得來(lái)未桥,兩者沒(méi)有本質(zhì)上的區(qū)別笔刹,他只是描述出了問(wèn)題,然后想知道答案冬耿,只要答案的可信程度夠高舌菜,他就達(dá)到了自己的目的,半個(gè)月之后亦镶,機(jī)翼制作完成日月,他可以做實(shí)機(jī)試飛實(shí)驗(yàn)了,他早已經(jīng)忘記那個(gè)微不足道的微分方程染乌,但是某一天山孔,他可能又要重新?lián)炱饋?lái),他可能已經(jīng)記錄在了某個(gè)記事本上荷憋,但是他已經(jīng)忘記是哪個(gè)本子了台颠,但是他有一個(gè)好習(xí)慣,就是給自己的筆記本寫(xiě)時(shí)間勒庄,只要翻閱某段時(shí)間內(nèi)的筆記串前,他肯定可以找到它,我們發(fā)現(xiàn)了某個(gè)找回信息的普遍的方法論实蔽,我們通過(guò)給信息添加標(biāo)簽荡碾,它只要是某個(gè)類型的信息,可以有不同的查詢方式局装,這樣就可以把固定查找的過(guò)程坛吁,轉(zhuǎn)化為模糊查詢的過(guò)程劳殖,我們只是想要答案,并且定位它拨脉,給定了一個(gè)范圍以后哆姻,并不需要準(zhǔn)確地記住它的位置,需要的時(shí)候再去查詢玫膀,我們的大腦便可以降低思考的難度矛缨,我們?cè)賮?lái)看范圍到底是什么,最初范圍這個(gè)詞和類型是聯(lián)系到一起的帖旨,一個(gè)類型的概念箕昭,它包含了無(wú)數(shù)它的實(shí)例,這些實(shí)例都是它涵蓋的范圍解阅,我們只需要知道這個(gè)范圍落竹,它映射到更大更明確的內(nèi)涵,該微分方程屬于某個(gè)航天飛機(jī)項(xiàng)目瓮钥,這個(gè)項(xiàng)目屬于某段時(shí)間筋量,記事本都是這個(gè)時(shí)間的,所以我們肯定可以在那里找到它碉熄。
接下來(lái)是我們的另一個(gè)c語(yǔ)言程序,它表示了一個(gè)矩陣計(jì)算

void mul_matrix(int M,int N, int K,int**A,int**B,int**C){
  for (i = 0; i < M; i++){
    for (j = 0; j < N; j++){
          int sum = 0;
          for (m = 0; m < K; m++)
            sum = sum + A[i][m] * B[m][j];
          C[i][j] = sum;
    }
  }
}

看起來(lái)這個(gè)程序好像很簡(jiǎn)單肋拔,實(shí)際上它的編寫(xiě)隱含的都是機(jī)器的思維锈津,它理解起來(lái)是非常深?yuàn)W的,以至于很多人會(huì)看著它深思很久凉蜂,因?yàn)樗话魏蔚某渥愕母拍顑?nèi)涵琼梆,它就是數(shù)組,和矩陣扯不上任何關(guān)系窿吩,甚至和向量也沒(méi)有任何關(guān)系茎杂,它是如此晦澀和陌生,循環(huán)纫雁,加減乘除煌往,控制條件,這些原始的部分死死地連接到了一起轧邪,無(wú)法使用更好的辦法分開(kāi)它們刽脖,并且每一次的計(jì)算都寫(xiě)死在了算法給定的數(shù)組里,所以說(shuō)用c語(yǔ)言忌愚,甚至c++和java等編程曲管,它們是等價(jià)的,算法的部分無(wú)法用更好的方式分解硕糊,它們死死地耦合在一起院水,一個(gè)問(wèn)題無(wú)法被分解為某些相同的計(jì)算部分腊徙,從而讓每一次的思考都重復(fù)出現(xiàn),人的大腦提前計(jì)算了一次檬某,然后再進(jìn)入了計(jì)算機(jī)運(yùn)算昧穿。
實(shí)際上人腦思考問(wèn)題并不是這樣的方式,人腦擅長(zhǎng)模糊思考橙喘,通過(guò)一個(gè)概念來(lái)引用它的范圍时鸵,從而確定它包含的內(nèi)容,每一個(gè)計(jì)算都可以用足夠的信息來(lái)描述它的運(yùn)作方式厅瞎,把計(jì)算過(guò)程設(shè)計(jì)為通信饰潜,而不是計(jì)算,要獲取數(shù)據(jù)和簸,則向函數(shù)發(fā)送一條信息彭雾,它返回一個(gè)你需要的信息就是數(shù)據(jù),lambda演算很好的做到了這一點(diǎn)锁保,它并不執(zhí)著于把每一條數(shù)據(jù)都提前計(jì)算好薯酝,而是用到的時(shí)候再拿出來(lái),由于具有足夠多的描述信息來(lái)確定它的范圍爽柒,所以確定內(nèi)容的時(shí)候再計(jì)算吴菠,可以避免提前把每一個(gè)數(shù)據(jù)都演算一遍,于是大大降低了思考的難度浩村,這是用scheme實(shí)現(xiàn)同樣的c代碼的功能

(define (mul-vec v1 v2)
  (if (= (length v1) (length v2))
      (if (null? v1) 0
      (+ (* (car v1) (car v2))
         (mul-vec (cdr v1) (cdr v2))))
      'error))

(define (make-matrix row col data)
  (lambda msg
    (if (= (length msg) 1)
    (cond
     ((eq? (car msg) 'row) row)
     ((eq? (car msg) 'col) col))
    (list-ref data (+ (* (car msg) col) (cadr msg))))))

(define (col-ref matrix col-index)
  (define (col-loop matrix row-index)
    (if (= row-index (matrix 'row))
    '()
    (cons (matrix row-index col-index) (col-loop matrix (+ row-index 1)))))
  (col-loop matrix 0))

(define (row-ref matrix row-index)
  (define (row-loop matrix col-index)
    (if (= col-index (matrix 'col))
    '()
    (cons (matrix row-index col-index) (row-loop matrix (+ col-index 1)))))
  (row-loop matrix 0))

(define (mul-matrix A B)
  (if (= (A 'col) (B 'row))
      (lambda msg
    (if (= (length msg) 1)
        (cond
         ((eq? (car msg) 'row) (A 'row))
         ((eq? (car msg) 'col) (B 'col)))
        (mul-vec (row-ref A (car msg)) (col-ref B (cadr msg)))))
      (lambda msg 'error)))

代碼好像長(zhǎng)了很多做葵,但是實(shí)際上上面的代碼可以用語(yǔ)法糖的形式實(shí)現(xiàn)

(m ([mx i j _] * [mx j k _]) n) = (m [mx i j _]) * ([mx j k _] n)

主要的mul-matrix函數(shù)只做了一個(gè)事情,把A的行和B的列進(jìn)行向量相乘的函數(shù)作為一個(gè)計(jì)算結(jié)果心墅,每一次使用該函數(shù)酿矢,就是獲取它的元素,由于之前我們把list等等都實(shí)現(xiàn)為lambda函數(shù)怎燥,所以相乘之后的矩陣函數(shù)實(shí)際上性質(zhì)上等同于make構(gòu)造的函數(shù)瘫筐,整個(gè)系統(tǒng)無(wú)縫地揉合在了一起,然而我們只是描述了某些規(guī)則铐姚,它并沒(méi)有把所有的元素都計(jì)算好策肝,我們并不需要都去計(jì)算好,我們只是把A和B給了它谦屑,它自己知道怎么算出來(lái)驳糯,它描述了將要執(zhí)行的計(jì)算,這里沒(méi)有可怕的循環(huán)和大量的遞歸氢橙,只有足夠多的描述酝枢,它精確地描述了,矩陣的行和列到底是什么悍手,向量的乘法到底是啥帘睦,矩陣的定義到底是什么袍患,矩陣乘法的定義到底是什么,它就可以計(jì)算了竣付,它是如此地優(yōu)雅诡延,以至于讓人想到了萬(wàn)物的運(yùn)行狀態(tài),到底是一系列的狀態(tài)變化古胆,還是即將執(zhí)行的計(jì)算對(duì)象肆良,每一次計(jì)算都帶來(lái)了新的數(shù)據(jù),假如不計(jì)算逸绎,你無(wú)法確定它到底是什么狀態(tài)惹恃,每一次計(jì)算,你就可以知道它內(nèi)部到底有什么棺牧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末巫糙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子颊乘,更是在濱河造成了極大的恐慌参淹,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,657評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乏悄,死亡現(xiàn)場(chǎng)離奇詭異浙值,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)纲爸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,662評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門亥鸠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人识啦,你說(shuō)我怎么就攤上這事∩衩茫” “怎么了颓哮?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,143評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)鸵荠。 經(jīng)常有香客問(wèn)我冕茅,道長(zhǎng),這世上最難降的妖魔是什么蛹找? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,732評(píng)論 1 284
  • 正文 為了忘掉前任姨伤,我火速辦了婚禮,結(jié)果婚禮上庸疾,老公的妹妹穿的比我還像新娘乍楚。我一直安慰自己,他們只是感情好届慈,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,837評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布徒溪。 她就那樣靜靜地躺著忿偷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪臊泌。 梳的紋絲不亂的頭發(fā)上鲤桥,一...
    開(kāi)封第一講書(shū)人閱讀 50,036評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音渠概,去河邊找鬼茶凳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛播揪,可吹牛的內(nèi)容都是我干的贮喧。 我是一名探鬼主播,決...
    沈念sama閱讀 39,126評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼剪芍,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼塞淹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起罪裹,我...
    開(kāi)封第一講書(shū)人閱讀 37,868評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤饱普,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后状共,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體套耕,經(jīng)...
    沈念sama閱讀 44,315評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,641評(píng)論 2 327
  • 正文 我和宋清朗相戀三年峡继,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冯袍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,773評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碾牌,死狀恐怖康愤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情舶吗,我是刑警寧澤征冷,帶...
    沈念sama閱讀 34,470評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站誓琼,受9級(jí)特大地震影響检激,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腹侣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,126評(píng)論 3 317
  • 文/蒙蒙 一叔收、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧傲隶,春花似錦饺律、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,859評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)腮出。三九已至,卻和暖如春芝薇,著一層夾襖步出監(jiān)牢的瞬間胚嘲,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,095評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工洛二, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留馋劈,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,584評(píng)論 2 362
  • 正文 我出身青樓晾嘶,卻偏偏與公主長(zhǎng)得像妓雾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子垒迂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,676評(píng)論 2 351