惰性求值編程
㈠ C#函數式編程中的惰性求值詳解
其實這個問題中有兩個不正確的地方:
第一C#編程中所謂的函數其實是成員方法,函數是SP(面向過程編程中的術語,在面向對象中不存在這樣的術語,只是很多程序員由SP轉到OOP上,所以才有這種不規則的稱呼)。有C#中沒有獨立到類之外的方法,也就是任何一個方法(包含靜態成員方法)都是屬於某個類或對象的。所以沒有函數之稱。
第二個是惰性其實並非C#術語,也非.net術語,勉強可算得上是面向對象的一種不規則稱呼,因為C#其實是靜態編譯而非動態編譯,直到.net 4。0之後才支持動態編譯(只是支持),而所謂的惰性其實指的是運算的結果延遲到使用時才會載入。所以是後期綁定的一種特殊形式,(一般認為早期綁定是指編譯態綁定,而後期綁定則是運行態綁定),所以在編程時應該理解什麼時早期綁定,什麼是後期綁定就可以了,而面各對象術語中還存在一種延遲載入的形式,就是所謂的「懶載入」又叫惰性載入。凡是具有這種特性的屬性或運算結果的,也有人叫惰性載入。
在設計模式中有一個單例模式,其實有一種就是「懶漢模式」,在第一次使用時才會真實初始化實例。在linq to sql中的lumbda表達式中的,有一種IEnumable<T>的形式,執行的結果中其實並沒有真正的結果,只有第一次引用該結果時才會真正載入結果,以供調用。類似延遲載入的模式就是所謂的情性載入,它的目的就是提高性能。
但是對於應用程序員來說,IQuery<T>和IEnumerable<T>是沒有任何區別的,基本上不需要去判斷兩者的任何區別,而追求性能與各方面的其他考慮的設計師或是高程才會考慮到兩者的具體的區別,所以基本上來說對於應用程序員不需要理解,只有高級程序員才會在性能上考慮諸多因素,如果內存等各方面原因時才會考慮到延遲載入。
㈡ java stream 及早求值
流使用的通用格式:
獲得流對象Stream
對流對象Stream進行惰性求值,返回值仍然是一個Stream對象。
對流對象Stream進行及早求值,返回值不在是一個Stream對象。
㈢ 如何評價 Racket 這門編程語言
Racket的誕生與發展
簡單介紹一下Racket的發展,詳見知乎的一個關於Racket的問題回答:
1958年,人工智慧之父John McCarthy 發明了一種以 Lambda 演算為基礎的符號處理語言,1960年 McCarthy 發表著名論文Recursive Functions of Symbolic Expressions and Their Computation by Machine, 從此這種語言被命名為 LSIP (List Processor),其語法被命名為:符號表達式(S-Expression)。LISP構建在7個函數[atom car cdr cond cons eq quote]和2個特型[lambda label]之上。
Lisp誕生之初是為了純粹的科學研究,代碼執行像數學公式一樣,以人的大腦來演算。直到麥卡錫的學生斯蒂芬·羅素將eval函數在IBM 704機器上實現後,才開啟了Lisp作為一種計算機語言的歷史。1962年,第一個完整的Lisp編譯器在MIT誕生,從此之後Lisp以MIT為中心向全世界傳播。之後十多年,出現了各種Lisp方言。
1975年,Scheme誕生。Scheme同樣誕生與MIT,它的設計哲學是最小極簡主義,它只提供必須的少數幾個原語,所有其他的實用功能都由庫來實現。在極簡主義的設計思想下,Scheme趨於極致的優雅,並作為計算機教學語言在教育界廣泛使用。
1984年,Common Lisp誕生。在二十世紀七八十年代,由於Lisp方言過多,社區分裂,不利於lisp整體的發展。從1981年開始,在一個Lisp黑客組織的運作下,經過三年的努力整合後,於1984年推出了Common Lisp。由於Scheme的設計理念和其他Lisp版本不同,所以盡管Common Lisp借鑒了Scheme的一些特點,但沒有把Scheme整合進來。此後Lisp僅剩下兩支方言: Common Lisp 和 Scheme。
從二十世紀九十年代開始,由於C++、Java、C#的興起,Lisp逐漸沒落。直到2005年後,隨著科學計算的升溫,動態語言JavaScript、python、Ruby的流行,Lisp又漸漸的回到人們的視線。不過在Lisp的傳統陣地教育界,Python作為強有力的挑戰者對Scheme發起沖鋒;在2008年,MIT放棄了使用Scheme作為教學語言的SICP(計算機程序的構造和解釋)課程,而啟用Python進行基礎教學。同時美國東北大學另立爐灶,其主導的科學計算系統PLT Scheme開始迅猛發展;2010年,PLT Scheme改名為Racket。近幾年,The Racket Language連續成為年度最活躍語言網站,並駕齊驅的還有haskell網站。
符號表達式 S-Expression
首先說一下S表達式:S-表達式的基本元素是list與atom。list由括弧包圍,可包涵任何數量的由空格所分隔的元素,原子是其它內容。其使用前綴表示法,在Lisp中既用作代碼,也用作數據。如:1+2*3 寫成前綴表達式就是(+ 1 (* 2 3)) 。
優點:容易parse,簡單純粹,不用考慮什麼優先順序等,也是實現代碼即數據的前提;
缺點:可讀性不是很強;
高階函數
高階函數至少滿足下列一個條件:
接受一個或多個函數作為輸入;
輸出一個函數;
微積分中的導數就是一個例子,映射一個函數到另一個函數。在無類型 lambda 演算中,所有函數都是高階的。在函數式編程中,返回另一個函數的高階函數被稱為Curry化的函數。Curry化即把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,並且返回接受餘下的參數而且返回結果的新函數的技術。如 f(x,y)=x+y, 如果給定了 y=1,則就得到了 g(x)=x+1 這個函數。
Lambda 表達式
Racket中實用Lambda表達式來定義匿名函數,《如何設計程序》書中給出的使用原則是:如果某個非遞歸函數只需要當參數使用一次,實用Lambda表達式。如果想用Lambda表達式來表達遞歸,就需要引入Y組合子,Y 就是這樣一個操作符,它作用於任何一個 (接受一個函數作為參數的) 函數 F,就會返回一個函數 X。再把 F 作用於這個函數 X,還是得到 X。所以 X 被叫做 F 的不動點(fixed point),即 (Y F) = (F (Y F)) 。
惰性求值
惰性求值(Lazy Evaluation),說白了就是某些中間結果不需要被求出來,求出來反而不利於後面的計算也浪費了時間。參見:惰性求值與惰性編程。
惰性求值是一個計算機編程中的一個概念,它的目的是要最小化計算機要做的工作。惰性計算的最重要的好處是它可以構造一個無限的數據類型。使用惰性求值的時候,表達式不在它被綁定到變數之後就立即求值,而是在該值被取用的時候求值。語句如 x:=expression; (把一個表達式的結果賦值給一個變數)明顯的調用這個表達式並把計算並把結果放置到 x 中,但是先不管實際在 x 中的是什麼,直到通過後面的表達式中到 x 的引用而有了對它的值的需求的時候,而後面表達式自身的求值也可以被延遲,最終為了生成讓外界看到的某個符號而計算這個快速增長的依賴樹。
閉包
閉包在計算機科學中,閉包(Closure)是詞法閉包(Lexical Closure)的簡稱,是引用了自由變數的函數。自由變數是在表達式中用於表示一個位置或一些位置的符號,比如 f(x,y) 對 x 求偏導時,y就是自由變數。這個被引用的自由變數將和這個函數一同存在,即使已經離開了創造它的環境也不例外。在函數中(嵌套)定義另一個函數時,如果內部的函數引用了外部的函數的變數,則可能產生閉包。運行時,一旦外部的 函數被執行,一個閉包就形成了,閉包中包含了內部函數的代碼,以及所需外部函數中的變數的引用。其中所引用的變數稱作上值(upvalue)。網上有很多將JavaScript閉包的文章,如果你對LISP有系統的了解,那麼這個概念自然會很清楚了。
快排的Racket實現
#langracket
(define(quick-sortarray)
(cond
[(empty?array)empty];快排的思想是分治+遞歸
[else(append
(quick-sort(filter(lambda(x)(<x(firstarray)))array));這里的array就是閉包
(filter(lambda(x)(=x(firstarray)))array)
(quick-sort(filter(lambda(x)(>x(firstarray)))array)))]))
(quick-sort'(132534509824))
;;運行結果'(012334455982)
通過這個例子,就可以感受到基於lambda運算元的 Racket 語言強大的表達能力了,高階函數、lambda表達式和閉包的使用是Racket所描述的快排十分的精煉,這和 基於馮諾依曼模型C語言是迥然不容的思維模式。後面,隨著Racket 學習的進一步深入,嘗試寫一下解釋器
㈣ python中and、or和not 三個邏輯運算符,一直理解不了,求幫助!
『and』、『or』和『not』的優先順序是not>and>or
㈤ F#是一種什麼樣的語言
F#是由微軟發展的為.NET語言提供運行環境的程序設計語言,是函數編程語言,函數編程語言最重要的基礎是Lambda Calculus。它是基於OCaml的,而OCaml是基於ML函數編程語言。有時F#和OCaml的程序是可以交互編譯的。
F#已經接近成熟,支持高階函數、柯里化、惰性求值、Continuations、模式匹配、閉包、列表處理和元編程。這是一個用於顯示.NET在不同編程語言間互通的程序設計,可以被.NET中的任意其它代碼編譯和調用。
㈥ 惰性編程和惰性求值
惰性編程是一種將對函數或請求的處理延遲到真正需要結果時進行的通用概念。有很多應用程序都採用了這種概念,有的非常明顯,有些則不太明顯。從惰性編程的角度來思考問題,可以幫您消除代碼中不必要的計算,也可以幫您重構程序以使之更加面向問題。
Scheme 中的簡單惰性編程
惰性編程是這樣一種技術:它可以將代碼的求值延遲到需要結果值時再進行。例如,在 Scheme 中,惰性編程就是通過兩個特殊的結構顯式支持的。Scheme 的 delay 特殊表單接收一個代碼塊,它不會立即執行它們,而是將代碼和參數作為一個 promise 存儲起來。如果您 force 這個 promise 產生一個值,它就會運行這段代碼。promise 隨後會保存結果,這樣將來再請求這個值時,該值就可以立即返回,而不用再次執行代碼了。
下面是一個說明 delay 和 force 如何一起工作的簡單例子。
清單 1. 使用 delay 和 force 的例子
;;The multiplication is saved but not executed
(define my-promise (delay (* 5 6 7 8 9)))
;;Again, saved but not executed
(define another-promise (delay (sqrt 9)))
;;Forces the multiplication to execute. Saves and returns the value
(display (force my-promise))
(newline)
;;This time, the multiplication doesn't have to execute. It just returns
;;the value that was previously saved.
(display (force my-promise))
(newline)
;;This proces an error, because the promise must be forced to be used
(display (+ my-promise (force another-promise)))
這些結構的使用都非常簡單,但是它們應該用來干什麼呢?一般地,惰性編程常用於所調用的函數生成調用程序不需要的值的情況下。例如,假設有一個函數會計算一組數字的平均值、方差和標准差。下面是實現這些功能的一種方法:
清單 2. 簡單的統計計算
(define (square x) (* x x))
(define (calculate-statistics the-series)
(let* (
(size (length the-series))
(sum (apply + the-series))
(mean (/ sum size))
;variance is the sum of (x[i] - mean)^2 for all list values
(variance
(let* (
(variance-list (map (lambda (x) (square (- x mean))) the-series)))
(/ (apply + variance-list) size)))
(standard-deviation (sqrt variance)))
(vector mean variance standard-deviation)))
;Run the program
(display (calculate-statistics '(2 6 4 3 7 4 3 6 7 8 43 4 3 2 36 75 3)))
(newline)
現在假設我們希望只在特定條件下才會計算標准差,但是這個函數卻早已花費了很多計算能力來計算標准差。有幾種方法可以解決這個問題。您可以將其分割成幾個單獨的函數分別計算平均值、方差和標准差。不過,這樣每個函數都必須重復計算平均值的過程。如果您同時需要這三個值,那麼平均值就會被計算 3 次、方差會被計算 2 次、標准差會被計算 1 次。這中間有很多額外的不必要的工作。現在,您也可以要求將平均值傳遞給標准差函數,並強制用戶為您調用平均值的計算函數。盡管這是可行的,但是這會產生一個非常可怕的 API:它的介面使用了太多特定於實現的內容。
使用惰性求值的一種較好的方法是將計算延遲:
清單 3. 利用惰性求值進行統計計算
(define (square x) (* x x))
(define (calculate-statistics the-series)
(let* (
(size (delay (length the-series)))
(mean (delay (/ (apply + the-series) (force size))))
;variance is the sum of (x[i] - mean)^2
(variance
(delay
(let* (
(variance-list (map (lambda (x) (square (- x (force mean))))
the-series)))
(/ (apply + variance-list) (force size)))))
(standard-deviation (delay (sqrt (force variance)))))
(vector mean variance standard-deviation)))
;Run the program
(define stats (calculate-statistics '(2 6 4 3 7 4 3 6 7 8 43 4 3 2 36 75 3)))
(define mean (force (vector-ref stats 0)))
(define variance (force (vector-ref stats 1)))
(define stddev (force (vector-ref stats 2)))
(display (list mean variance stddev))
(newline)
在這個版本的 calculate-statistics 中, 直到真正需要值時才會進行計算,並且同樣重要的是,任何值都只會計算一次。如果您首先請求計算方差,它就會先計算平均值,並將其保存 下來,然後再計算並保存方差。如果接下來您請求計算平均值,因為平均值已經計算出來了,所以只需要簡單地返回該值即可。如果您接下來請求計算標准差,它就會使用保存過的方差值,並通過此值來計算標准差。現在不會執行任何不需要的計算,函數的介面也得到了很好的保留。
在面向對象語言中,這種惰性編程方法也非常容易實現。在任何需要一組相關計算的地方,都可以創建一個類來保存中間值。構造函數接收所使用的初值,所有計算出來的值都被設置為空。這里不再使用 force,相反地,每個值都有一個 getter,它會檢查該值是否為空;如果該值不為空,就執行計算。下面是 Java™ 語言中這種類的一個骨架:
清單 4. Java 語言中惰性求值的形式化表示
public class StatisticsCalculator {
private List the_series;
private Double mean;
private Integer size;
private Double variance;
private Double standard_deviation;
public StatisticsCalculator(List l)
{
the_series = l;
}
public int getSize()
{
if(size == null)
{
size = the_series.size();
}
return size;
}
public double getStandardDeviation()
{
if(stddev == null)
{
stddev = Math.sqrt(getVariance());
}
return stddev;
}
...
...
}
這個類本身可以作為一個多值的 promise 使用,實例變數中保留了計算的結果。每個函數都會通過查看變數值是否為空來確定代碼是否已經執行過了。如果變數在需要時尚未有值,就執行代碼並保存計算結果。注意如果 null 也在該值的有效值范圍內,那麼就需要一個輔助標志來說明代碼是否已經執行過了,而不能簡單地查看該值是否為空。
正如您所見,惰性編程技術可以用來為那些返回相關值的函數提供良好有效的 API。另外,在那些不直接支持惰性編程的語言中,惰性編程技術也可以通過類來實現。
不確定列表
假設您有一個由 5 的倍數組成的列表。現在您希望計算這個列表中所有數字的平方。最後,我們希望對計算結果進行遍歷,並顯示所有平方值小於 500 的數字。什麼?您不能對一個無窮列表進行操作?為什麼不行呢?
實際上,可能與直觀的感覺相反,如果無窮列表基於一定的生成規則 ,那麼無窮列表可能比有窮列表存儲起來佔用的空間更少。在上面這個例子中,原始列表是基於 X[i] = (i + 1) * 5 這條規則生成的,其中 X[i] 是列表在索引 i 處的值。 X[i] 也可以使用其前一個值來表示:X[i] = X[i-1] + 5; X[0] = 5。使用 Scheme 的 force 和 delay,就可以構造一個基於這條規則的數值流:
清單 5. 流的例子
;This is the generative rule for the list. It returns a pair
;with the car being the next value, and the cdr being a promise
;for the next pair
(define (my-generative-rule last-val)
(let (
;generative formula based on previous value
(next-val (+ last-val 5)))
;put the next value together with a promise for another one
(cons next-val (delay (my-generative-rule next-val)))))
;Since the cdr is a promise of a pair, rather than a pair itself,
;we have our own functions for getting the car and cdr.
(define (mystream-car the-stream) (car the-stream))
(define (mystream-cdr the-stream) (force (cdr the-stream)))
;Create our list
(define multiples-of-five (cons 5 (delay (my-generative-rule 5))))
;Display the fourth element of the list
(display (mystream-car (mystream-cdr (mystream-cdr (mystream-cdr multiples-of-five)))))
(newline)
現在,您希望計算所有數字的平方。要實現這種功能,需要一個函數從現有流和生成規則中創建一個新流 —— 這有點像 map,只不過它是針對流進行的。函數如下:
清單 6. 流的專用映射
(define (mystream-map function the-stream)
(cons
;;The first value will be the function applied to the car
(function (car the-stream))
;;The remaining values will be stored in a promise
(delay (mystream-map function (mystream-cdr the-stream)))))
(define squared-stream (mystream-map (lambda (x) (* x x)) multiples-of-five))
;Display the fourth element of this new list
(display (mystream-car (mystream-cdr (mystream-cdr (mystream-cdr squared-stream)))))
(newline)
現在,剩下的問題就是循環遍歷該流,並列印結果值小於 500 的值:
清單 7. 循環遍歷流
(let loop (
(remaining-stream squared-stream))
(if (>= (mystream-car remaining-stream) 500)
#f
(begin
(display (mystream-car remaining-stream))
(newline)
(loop (mystream-cdr remaining-stream)))))
顯然,對於這種小程序來說,還有很多方法都可以更直接地實現這個目標。然而,即使在這個例子中,流也可以幫助我們較少地從實現角度來審視問題,而是更多地將問題當作一種抽象思想來看待。流讓我們可以集中精力在問題 上,而不是在實現 上。流簡化了與生成元素有關的演算法的概念和實現。
到目前為止,我們所討論的流對於學習流背後的概念來說都非常有用。然而,實現卻可能會經受大量缺陷的折磨。首先,在很多場合下流都可能需要一個終結器。這種實現卻並沒有提供這種機制。另外,此處給出的這種流稱為 odd stream,這種形式的流很容易實現。 但是這種流可能會導致所執行的計算量比所希望的多,因為列表中的值都會進行計算。標准流是在 SRFI-40 中定義的,它解決了這些問題以及其他一些問題(更多細節請參看 參考資料)。
回頁首
跳過轉換變數
到目前為止,我們所有惰性求值的例子都要求在進行計算之前必須完全實現中間值。部分原因是由於我們正在解決的問題的需求所導致的,另外一部分原因則是由於 delay 和 force 操作本身所帶來的。例如,考慮下面的代碼:
(define val1 20)
(define val2 30)
(define val3 40)
(define val4 0)
(define val5 (delay (* val1 val2)))
(define val6 (delay (* val4 val3)))
(define val7 (* (force val5) (force val6)))
在這個例子中,我們知道答案是 0,這是因為我們知道 0 乘任何次都是 0。因此,盡管這看上去似乎是可以使用惰性求值的情況(因為我們正在試圖減少不必要的計算),但實際上使用 delay 和 force 並不能給我們提供什麼幫助。
在這類情況下,為了不生成中間結果,需要一些特殊的惰性演算法來延遲處理。我們需要實現對請求進行排隊。然後,在請求最後結果時,它就可以在該隊列中搜索那些可以幫助它對處理過程進行優化的值,然後使用這些值跳過對其他值的處理。在乘法的這個例子中,出現 0 就可以跳過所有的處理了。
下面是一個特殊的 delay/force 對,專門適用於乘法計算:
清單 8. 簡單的專用 delay/force 對
;This will simply use a list to keep track of the values to be multiplied
(define (multiply-delay x y)
(let (
;convert to lists if they aren't already
(x-list (if (list? x) x (list x)))
(y-list (if (list? y) y (list y))))
;append them together
(append x-list y-list)))
(define (multiply-force mul-list)
(let (
(has-zero? #f))
(for-each
(lambda (x)
(if (eqv? 0 x)
(set! has-zero? #f)
#f))
mul-list)
(if has-zero?
0
(apply * mul-list))))
(define a (multiply-delay 23 54))
(define b (multiply-delay 0 5))
(define c (multiply-delay a b))
(define d (multiply-delay c 55)
(display (multiply-force d))
(newline)
然而,這個實現也有自己的問題。現在各個部分都不再是惰性的了,也不會再保存值了。為了實現一個優化,我們已經失去了原來的 delay 和 force 的優點。因此,我們不會保留一個所有參數的長列表,而是需要將它們放到各個 promise 中單獨進行存放,這樣我們就依然可以獲得之前的優點。我們所需要的是一個說明該值是否已經計算的標記。如果該值已經計算過了,那麼此處就只有一個不需要計算的元素了。否則,乘數和被乘數都會出現。新的代碼如下:
清單 9. 另外一個專用的惰性結構
;Unevaluated promises will have the structure ('delayed val1 val2)
;Evaluated promises will have the structure ('forced result)
;Create an unevaluated promise
(define (multiply-delay x y)
(list 'delayed x y))
(define (multiply-force-nozero promise)
(if (pair? promise)
(if (eq? (car promise) 'forced)
;if the promise has been forced, just return the value
(cdr promise)
;otherwise, compute the value, and convert this into a "forced" promise
(begin
(set-car! promise 'forced)
(set-cdr! promise
(*
(multiply-force-nonzero (cadr promise))
(multiply-force-nonzero (caddr promise))))
;return the forced value
(cdr promise)))
;This is just a number, so return it
promise))
這個結構中有普通 delay/force 的所有優點。現在,由於乘法操作不管怎樣都是一個相當快速的操作,因此這個完整的操作可能就有點浪費時間,不過它作為一個例子來說還是非常不錯的。它對於執行代價更為昂貴的操作來說可以節省大量的時間,尤其是對於那些可能需要上下文開關或大量處理器資源的操作來說更是如此。
這種技術一種流行的用法是進行字元串的附加操作。它不用在每次進行附加操作時都分配新空間,而是只需要簡單地維護一個需要進行連接的字元串列表。然後,當需要最終版本的字元串時,就只需要為這個新字元串分配一次空間。這樣就節省了多個 malloc 調用,以及復制每個字元串的時間。
惰性求值
到現在為止,我們重點介紹的是非惰性語言中的惰性結構。然而,有些語言,例如 Haskell,會將一些惰性操作作為普通求值過程的一部分。這被稱之為惰性求值。惰性求值中的參數直到需要時才會進行計算。這種程序實際上是從末尾開始反向執行的。它會判斷自己需要返回什麼,並繼續向後執行來確定要這樣做需要哪些值。基本上,每個函數都是使用對各個參數的 promise 來調用的。當計算需要值時,它就會執行 promise。由於代碼只有在需要值時才會執行,因此這就稱為按需調用。在傳統編程語言中,傳遞的是值,而不是 promise,因此這就稱為按值調用。
按需調用編程有幾個優點。流是自動實現的。不需要的值永遠也不會計算。然而,惰性程序的行為通常都很難預測。而在按值調用程序中,求值的順序是可以預測的,因此任何基於時間或序列的計算都相當容易實現。在惰性語言中這就變得相當困難了,此時要描述具有明確順序的事件就需要諸如 monads 之類的特殊結構。所有這些都使得與其他語言的交互變得十分困難。
有幾種語言默認就會進行惰性編程,包括 Haskell 和 Clean。其他幾種語言也有一些惰性版本,包括 Scheme、ML 等。
㈦ javascript 惰性求值
惰性編程是一種將對函數或請求的處理延遲到真正需要結果時進行的通用概念。有很多應用程序都採用了這種概念,有的非常明顯,有些則不太明顯。從惰性編程的角度來思考問題,可以幫您消除代碼中不必要的計算,也可以幫您重構程序以使之更加面向問...
㈧ 最近有什麼好看的電影嗎求推薦
最近上映的《神奇女俠》《木乃伊》《變形金剛》和《小蜘蛛俠》都算是不錯的商業影片,感興趣的話推薦你去看看。
㈨ 什麼是函數式編程思維
回答都有跑題,show概念之嫌,題主問的是函數式思維,這個問題我一直在思考,畢竟是方法論,能力有限,只能從切身實踐告訴你
1.表達式化
在
最初的時候,需要轉變觀念,去可變數,去循環,把命令式改成表達式,注意,這只是把你丟在荒山野嶺讓你感受一下,離開熟悉的環境,地球依然在轉,但是有個
重點,那就是一切都是表達式; 為什麼是表達式呢?這個問題就像為什麼魚在水裡?
因為函數式建立在lambda演算之上而非圖靈機,只不過兩者被證明等價,所以你可以在你的機器上跑全是表達式的代碼,就如有人證明天空適合魚生存,所以
魚可以在天上游
當你接受了魚可以在天上游之後,就該上正餐了
1.5 數據與行為分離
這也是和面向對象不一致的地方,面向對象強調數據與行為綁定,但函數式不是,確切的說函數式 函數與數據等價,所以你才可以將函數當參數與返回值,你在設計時,切勿讓數據自己長腿能跑,其次,行為必須消除副作用,不可以偷偷把數據改了,習慣第一條後,應該不會的
2.高階邏輯
用
了函數式,就不要在想循環,賦值這些低階邏輯了,而應該更高階的思考問題,這比轉化表達式更難,函數式又叫聲明式,也就是你要做什麼,只要說一下就行,而
非寫個遍歷,做個狀態判斷,用函數式你不需要考慮這些,你不知道函數式的列表是怎麼遍歷的,中間向兩邊?
從後往前?這也是為何函數式適合並發的原因之一,你想知道列表中大於3的數有多少,只要,list.count(_ > 3)
而不是寫循環,你可以直接寫你的業務,不要拘泥於細節,有點像sql, 你需要什麼告訴電腦就行,你或許會問,count foreach filter
這些函數怎麼來的? 因為有了他們你才不需要寫循環,他們把你留在高階邏輯中,這個問題的答案請看下面
3.組合子邏輯 或又叫 自底向上的設計
函
數式和OO是反的,面向對象是自頂向下的設計,函數式是自底向上的設計,也就是先定義最基本的操作,然後不斷組合,不斷堆積以滿足你的所有需要,如sql
定義了select, from, where...這幾個組合子,來滿足你的查詢需求,同理函數式語言會提供foreach,
map等組合子(操作)來滿足你的需求,所以你必須自下而上的設計你的代碼結構,並且滿足你的需求,當你只用組合子寫代碼時,你會發現你寫的全是高階邏輯
如
果這些已有組合子滿足不了你,你就得自己寫,foreach不行,你就自己寫遞歸,我告訴你,遞歸背後也是組合子,這里一些'大神'應該不知道,在圖靈機
里,遞歸就是方法不斷調用自己沒什麼好說的,但是在lambda演算中,匿名函數是沒法調用自己的,所以遞歸是用Y組合子(又叫不動點組合子)把遞歸函數
自己求解出來再調用的,這才可以實現遞歸,並與圖靈機的循環等價,有點跑題了,總之要想順手的寫函數式,最好用面向組合子的設計,注意,不是必須,組合子
演算和lambda演算可以相互轉化,也就是,你完全可以寫一堆雜亂的表達式,但沒有組合子邏輯來得清爽,Haskell大規模使用monad這個特殊組
合子,始其變得統一整潔
好了,總結一下
函數式思維,其實就是組合子邏輯,用簡單的幾個函數組合來構建復雜邏輯,始終以高階的角度去表達問題,而非依賴副作用。
知道這點,你用java也可以寫函數式代碼了
但是,這也只是本人積累得來的感悟,絕不敢大肆伸張這就是函數式,我也在不斷研究中,如有問題,還望大神指正
㈩ mlranda中文是什麼意思
1、Miranda, 人名,米蘭達。
2、Miranda,一種編程語言,採用惰性求值的純粹函數編程語言,由英國學者大衛·特納(David Turner)所設計。採用來自ML語言與Hope語言的概念,他用此來作為他先前所設計的SASL語言與KRC語言的後繼者。稍後發展出的Haskell語言,採用了很多由Miranda所發展出的概念。
