在看定義的時候發覺以前有些概念混淆了,其實要掌握的高階概念就幾個,問題是容易忘記啊……
First-class object:
在PL里,它們是能被作為參數傳遞、作為右值、作為函數返回值的實體,以及最重要的,能在運行時被動態生成。
如C++里,int就是一個First-class Object,而函數不是,而是second-class object,雖然使用函數指針可以符合大多數特徵,但一個函數不能在運行時動態生成。
High order function,closure:
比一般函數nb,可以接受一個函數做參數,或者返回一個函數。如map/reduce/filter就是干這事的。
返回的函數稱為closure,注意這種說法不精確但卻比較容易理解。
準備來說,函數是編譯時已經確定的一段靜態可重用代碼段(例程),而閉包則是程序動態運行時新生成的代碼段。
雖然用起來是一樣的,但是原理機制卻不一樣。
lambda calculus & anonymous function:
calculus是演算的意思。propositional calculus是對一堆命題的演算,那麼lambda calculus其實就是關於lambda的演算。
lambda是什麽?應該可以理解為函數吧。以前的calculus操作的對象,可能是集合、整數、命題等,在這裡操作的對象則是函數。
lambda calculus是一個形式系統,可以用於研究函數的定義、遞歸、可計算等一系列問題。不過沒有系統學習過,我也不是很懂啦。
事實上涉及到理論的東西不需要太多,只需要知道有一個operator,稱為lambda,它的作用是生成函數,也就是說,函數可以被動態地創建,這對於函數作為"一等公民"很重要。通過lambda算子寫出來的函數與通過常規的define定義的函數幾乎沒兩樣,只是這個函數沒有名字而已,因此成為匿名函數,要使用它必須賦值給某個變量以引用。
如:
lambda x,y: x+y
則返回(生成)了一個函數,這個函數接受兩個參數,並且返回它們的和。
(lambda x,y: x+y)(20,30) 返回 20+30=50
再廢話一下,平時所用的命令式語言是直接建立在物理模型——即馮·諾依曼架構機器之上的語言,而它是圖靈機的近似。
而函數式語言可以被看做不考慮物理模型,而建立在理論模型——lambda calculus上的,所以思考的方式應該是函數。
lazy evaluation
很多functional language會用的東西。對立面是eager evaluation.
說白了,就是evaluate-on-demand,
即形如a=func(2)+2*7,普通語言執行到這句,a的值會馬上被計算。
但在lazy evaluation的語言中,它不會馬上被求值,而是先記錄dependancy,到真正引用a時才計算它的值。
考慮這樣一個應用:分別賦予a,b,c三個表達式,當用戶輸入1,2,3時輸出對應a,b,c的值。
這時如果用戶只輸入了一個2,顯然計算a,c的值很沒必要。所以這對程序的運行很有好處,節省計算資源。
另外一個許多語言都常見的類似lazy evaluation的情況是短路求值 (short-circuit evalution)
即在一個boolean expression如合取範式 a && b && c中,
如果a是false,則b與c的值也不必計算了,這也能看作lazy evaluation的一個特例。
--------------------------------
下麵三個比較重口味:
Curry(ing)
在我們計算一個接受多個參數的函數時(這裡請看做解數學題謝謝),如計算一個函數f(x,y,z)的值時,
如果我們暫時只知道z的值為3,則我們可以先把z代入f,得到現在的最新結果f(x,y,3)。
如果x=2,則進一步更新為f(2,y,3);
最後,當y的值確定為4時,函數所有參數齊全得到f(2,4,3) = ....(表達式求值)
因此,currying可以看做這麼一種技術(變換),它把一個接受多個參數的函數分解為多個接受單一參數的函數;
并且返回接受余下的参数而且返回结果。比如上例,當我們知道z=3后,f被curry化為g(x,y)=f(x,y,4)
顯然,在一個支持閉包與高階函數的語言里,要實現它是簡單的。
比如對於f(x,y,z),可以定義為,如果輸入參數小於應有參數,則返回一個新的函數,用已知的參數代入原函數
而接受未知參數;否則,計算表達式并返回值。新得到的函數同樣具有這個屬性。
curry化的具体的应用,我还没搞清楚……
continuation
monad
这两个我还是一知半解……赞不说明......
1 comment:
Post a Comment