<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    冒號課堂§4.1:函數(shù)范式

     

    冒號課堂

    第四課 重溫范式(1)


    課前導(dǎo)讀

    本課對函數(shù)式編程與邏輯式編程作了更詳細(xì)的展開,并對前面介紹的范式進(jìn)行了匯總分析,最后用情景式編程貫穿所學(xué)范式。

    本課共分四節(jié)——

    1. 函數(shù)范式
    2. 邏輯范式
    3. 匯總范式
    4. 情景范式

    4.1函數(shù)范式——精巧的數(shù)學(xué)思維

    知不知,上;不知不知,病                            ——《老子·德經(jīng)》

     

    關(guān)鍵詞:編程范式,函數(shù)式編程,Haskell,Groovy

    摘要:   再談函數(shù)式編程

     

     ?提問


    • 掌握編程范式對編程語感的提高有什么作用?
    • 為什么聲明式程序一般比命令式程序更簡潔?
    • 函數(shù)式編程有哪些特征?為何簡潔而不失強(qiáng)大?
    • 函數(shù)的無副作用性的意義何在?
    • 相比過程式和OOP,函數(shù)式編程的弱點是什么?

      講解
     

    眾人落座之后,冒號鳴鑼開場:“上兩節(jié)課為大家介紹了多種編程范式,雖未將所有類型盡囊其中,但最具代表性的均在其列。我們也不必貪多求全,俗話說得好:貪多嚼不爛啊?,F(xiàn)在給大家一個知識反芻的機(jī)會。”

    問號正感求之不得:“總算可以喘口氣了。我們就像觀光客,被導(dǎo)游帶著從一個景點趕往另一景點。一天下來,雖然大開眼界,但都是走馬觀花,無法充分領(lǐng)略各地的風(fēng)光。”

    “你說得沒錯,我就是那個不近情理的導(dǎo)游。”冒號哈哈一笑,“類似時下流行的歐洲NM日游,大部分人的收獲就是一堆照片和日漸模糊的記憶。不出多日,如果不看標(biāo)注,八成連照片上的背景是在法國還是在意大利都分不清了。”

    逗號頗有同感:“差不多,目前我的收獲就是一堆幻燈片和似懂非懂的概念。”

    冒號料有此果:“這一點也不奇怪。別說幾天游一個國家,單一個羅馬,沒有一個月是不可能深入了解的。至于編程范式,單一個OOP,沒有兩年以上的實踐和思考,是難以真正領(lǐng)會其精髓的。”

    嘆號深表懷疑:“OOP需要兩年以上才能領(lǐng)會?!”

    “那還得看你是否有足夠的勤奮和悟性。”冒號加強(qiáng)了語氣,“前面說過,單靠記憶只能觸及知識之表,單靠練習(xí)只能深入知識之里,唯有培養(yǎng)方能滲透知識之根。編程范式正處知識的根部,你們又怎能奢望只聽幾堂課即豁然貫通呢?”

    引號表達(dá)自己的感受:“雖然學(xué)了不少東西,但也存了不少疑惑,擱在心里有點不舒服。”

     “我明白你的意思。凡事追根究底是一種良好的學(xué)習(xí)習(xí)慣,也是一種可貴的學(xué)習(xí)精神。” 冒號表示理解和肯定,“但學(xué)習(xí)如打仗,除了要有直線式的縱深攻擊,還要有曲線式的迂回包抄?;仡櫸覀冎袑W(xué)的課堂,往往是每引入一個概念或理論,便圍繞其作深入的學(xué)習(xí)和反復(fù)的練習(xí)。在此過程中的種種疑惑,隨著學(xué)習(xí)的深入都會煙消云散。這樣穩(wěn)扎穩(wěn)打、層層推進(jìn),學(xué)得扎實,心里也踏實。但這種方法并不總是最好的,尤其在面臨動態(tài)的、開放的知識體系時,難免左支右絀。為此,我們必須學(xué)會適度地容忍無知。請注意,容忍無知不是放任無知,而是一種學(xué)習(xí)的技巧,讓無知成為求知的動力而不是障礙。容忍無知能使我們既不沮喪氣餒,也不急于求成。在學(xué)習(xí)時不妨略過一些細(xì)節(jié)或難點,先概覽全貌以獲取感性認(rèn)識,然后在逐步積累中升華為理性認(rèn)識。要而言之,我們不僅需要強(qiáng)調(diào)鉆勁和深度的‘釘子精神’,還需要強(qiáng)調(diào)磨功和廣度的‘刨子精神’。我一口氣兜售這么多編程范式,就是為了刺激大家求知欲,同時為大家進(jìn)行第一道刨磨。”

    引號得到一些安慰:“看來今后我們還會故地重游的。”

    “不僅會重游,而且會‘深度游’。” 冒號肯定地說,“此番我們一路行色匆匆,若能感受到途中景色帶來的感官沖擊,便算是不枉此行了。其實,把編程范式類比旅游景點并不十分準(zhǔn)確,或許比作當(dāng)?shù)氐娘L(fēng)俗文化更確切些。”

    句號立刻會意:“景點是具體的,背后的風(fēng)俗文化是抽象的;編程語言是具體的,背后的編程范式是抽象的。”

    “此乃其一。”冒號右手伸出食指,“其二,如果不了解景點的歷史文化和風(fēng)俗人情,僅僅滿足于表面的奇觀異景,一切很快便會淡忘。比如說,你若不了解圣經(jīng)文化、不了解文藝復(fù)興史,則歐洲之行至多只是視覺的盛宴,而非文化的洗禮,收獲將是有限的,印象將是膚淺的。同樣,如果你不了解編程范式,那么眼中的編程語言只是語法、語義、核心庫、規(guī)范等組成的集合,寫出的代碼雖能編譯、能工作,卻會顯得生硬、別扭。就像中式英語,語法正確、表達(dá)也正確,可就是不正宗、不地道。其癥結(jié)我們在第一節(jié)課中已經(jīng)提過了,即所謂的語感缺失。”

    問號實話實說:“可我還是不明白編程范式如何提高我們的編程語感。”

    “那就讓我們再說說范式吧。”冒號并不著急,“范式可以粗略理解為模范、模型、模式、風(fēng)格、流派等等。軟件中的范式除了編程范式外,還有架構(gòu)范式[1]、數(shù)據(jù)庫范式[2]等。比如,對象導(dǎo)向式(Object-Oriented)可以應(yīng)用于編程、架構(gòu)和數(shù)據(jù)庫中,分別成為OOPObject-Oriented Programming)、OOAObject-Oriented ArchitectureOODBobject-oriented database);類似地,事件驅(qū)動式(Event-Driven)可以是一種編程范式,可以是一種架構(gòu)模型,也可以是一種設(shè)計模式。總之,每種范式都代表著一套獨特而有效的解決問題的思想和方法。掌握范式對編程語感的提高至少有兩層作用:首先,編程語言的語法、語義等都是從編程范式的樹根衍生而出的枝葉,把握了這種脈絡(luò)和節(jié)奏,代碼才會如音樂舞蹈般韻律有致;其次,每種范式擅長的問題領(lǐng)域不盡相同,只有博聞廣識,方可揚長避短,程序才會如行云流水般流暢自然。”

    逗號添油加醋:“武功練至化境,一定是博采眾長,就像楊過融合了東邪、西毒、南帝、北丐、中神通等各派武功,才能隨心所欲地打出黯然銷魂掌來。”

    提起武俠人物,眾人俱是逸興遄飛,哪能體會到半點黯然消魂之傷?

    冒號道:“天下之理,殊途同歸。我們停止玄玄之論,用實例來說明吧。誰來介紹一下快速排序法(quicksort)?”

    眾人飛舞的思緒漸漸收斂,終于由引號作答:“快速排序法的思想是:在列表中找一個基準(zhǔn)元素,將所有小于它的元素劃歸一個子列,置于其前;將所有大于等于它的元素劃歸另一子列,置于其后。然后遞歸地對前后兩個子列作同樣處理,直至最終。”

    “很好,讓我們用Java來實現(xiàn)一下該算法。”冒號顯示出一段代碼——

    /** 快速排序法的Java實現(xiàn) */

    public class Sorter

    {

        public static <T extends Comparable<? super T>> void qsort(T[] list)

        {

            qsort(list, 0, list.length - 1);

        }

        private static <T extends Comparable<? super T>> void qsort(T[] list, int low, int high)

        {

            if (low >= high) return;

            int i = low - 1, j = high + 1;

            T pivot = list[low]; // 基準(zhǔn)元素

            for ( ; ; )

            {

                do { ++i; } while (list[i].compareTo(pivot) < 0);

                do { --j; } while (list[j].compareTo(pivot) > 0);

                if (i >= j) break;

                // 交換

                T tmp = list[i]; list[i] = list[j]; list[j] = tmp;

            }

            // 找到分割點j,遞歸

            qsort(list, low, j);

            qsort(list, j + 1, high);

        }

    }

    “請問這里用到了哪些編程范式?”冒號提問。

    嘆號心想,有何難哉?遂答:“既然是用Java實現(xiàn)的,自然少不了OOP。同時為了使算法更具普適性,還用到了泛型編程。”

    “你好像忘記了最重要的過程式,反倒是OOP的色彩極淡。”冒號顯然不滿意他的答案。

    嘆號不解:“不是說Java100%OOP語言嗎?”

    冒號頗為不屑:“不要輕信這種浮淺之論。且不說Java基本類型primitive type)不屬于class),本就不是100%OOP,即使是100%OOP,那與過程式也不矛盾啊。此例中的Sorter類連一個實例成員instance member)也沒有,唯一與OOP沾邊的是作為interfaceComparable,在C中也可用函數(shù)指針代替。如果不考慮泛型式的特征,本例無論用Java還是用C,并沒有本質(zhì)差別。事實上,對于這類純算法的問題,OOP范式本無太多用武之地。換句話說,quicksort雖然是通過以OOP著稱的Java來實現(xiàn)的,但用的主要還是過程式的思想和方法。”

    問號趕緊問道:“還能用其他范式來實現(xiàn)嗎?”

    此問正合冒號之意:“我們改用純函數(shù)式語言Haskell來試試——”

    -- 快速排序法的Haskell實現(xiàn)

    qsort :: (Ord a) => [a] -> [a] --函數(shù)聲明

    qsort[] = []                             --遞歸終點

    qsort(pivot : rest) = qsort[x| x <- rest, x < pivot]      --對前面的子列遞歸

                              ++ [pivot]

                              ++ qsort[x| x <- rest, x >= pivot]   --對后面的子列遞歸

    嘆號幾不能信:“竟然可以如此精煉?”

    “上面的Java代碼很難再精簡了,但與Haskell代碼相比還是太冗長了。后者省去了所有的賦值、迭代和流程控制,只有單純的遞歸,反映了典型的函數(shù)式特征。”冒號解說著,“鑒于你們對Haskell不太熟悉,我稍微解釋一下。第一步,聲明函數(shù)類型[3]:同類型列表之間的變換,其中Ord可類比Java中的Comparable,以保證列表元素之間能進(jìn)行比較;第二步,聲明遞歸終點:空列排序后仍是空列;第三步,描述遞歸原則:基準(zhǔn)元素pivot與剩余子列rest進(jìn)行排序后的列表,正是將小于基準(zhǔn)的子列和超過基準(zhǔn)的子列分別排序,中間插入基準(zhǔn)元素后的結(jié)果。”

    句號思有所得,不禁喜形于色:“我明白了,這兩段代碼生動地反映了命令式編程與聲明式編程之間的差別:前者需要指定計算的過程,后者只需指定計算的原則。一個著重微觀的細(xì)節(jié),一個著重宏觀的方向,自有繁簡之別。”

    冒號亦有所慰:“非常好!類似的話我以前也說過,但你們自己說的才是真正的收獲啊。我們還提過,過程式與函數(shù)式的差別同時也是機(jī)器思維與數(shù)學(xué)思維的差別。不妨對比Haskell表達(dá)式與數(shù)學(xué)中的集合表達(dá)式,它們是多么地相近!”

    黑板上出現(xiàn)兩行式子——

    數(shù)學(xué)表達(dá)式     {x| x rest, x < pivot}

    Haskell表達(dá)式[x| x <- rest, x < pivot]

    逗號仔細(xì)打量著:“嗯,的確像,跟哥倆似的,連符號<-都是仿照集合屬于符∈的。”

    “還有另一種表達(dá)方法。”冒號又添加了一行——

    Haskell表達(dá)式2(filter (< pivot) rest)

    “雖然與前一表達(dá)式的簡潔度相差無幾,但可讀性更強(qiáng)。filter即是過濾,將列表rest中的元素進(jìn)行篩選,條件是小于基準(zhǔn)元素。”冒號講解道。

    問號略感迷惑:“(< pivot)的用法看起來有點怪異。”

    “它是一個函數(shù),也是filter的第一個參數(shù),用來判斷第二個參數(shù)rest的元素是否合格,即 < pivot。這體現(xiàn)了函數(shù)式的一個重要特征:函數(shù)是頭等公民first-class citizen[4]——可作為傳遞參數(shù)、可作為表達(dá)式的值、可嵌入數(shù)據(jù)結(jié)構(gòu)、也可與某變量綁定,與普通的基本數(shù)據(jù)類型毫無二致。這是函數(shù)式編程簡潔而強(qiáng)大的重要根源。”冒號細(xì)加解釋,“大家還記得上節(jié)課談到的回調(diào)函數(shù)吧?callback無非是將函數(shù)作為參數(shù)來傳遞,本質(zhì)上是將代碼當(dāng)數(shù)據(jù)來使用,回調(diào)機(jī)制的巨大威力均拜此高級用法所賜。”

    眾人又一段筋脈被打通了。

    引號提出一個很實際的問題:“函數(shù)式編程的確很酷,可Java并不支持。如果采用Haskell之類的函數(shù)式語言,會不會帶來系統(tǒng)集成上的困難?”

    冒號打消了他的疑慮:“Java平臺下已經(jīng)集成了不少的支持函數(shù)式編程的語言,如JRuby、Jython、Groovy、Scala等,甚至HaskellJVM下也有相應(yīng)的Jaskell。其中GroovyJava的結(jié)合最為自然,我們看一下它是如何實現(xiàn)quicksort的——”

    /** 快速排序法的Groovy實現(xiàn) */

    def qsort(list) {

    if (list.size() <= 1) return list

    def pivot = list[0]

    return (qsort(list.findAll{x -> x < pivot})

          +            list.findAll{x -> x == pivot}

          +   qsort(list.findAll{x -> x > pivot}))

    }

    “雖然比Haskell的代碼略長了些,并且還帶著過程式的烙印,但總體思想還是函數(shù)式的。”冒號緊扣本質(zhì),“函數(shù)式還有一個重要特征:無副作用或盡量減少副作用[5]。所謂無副作用,是指一個函數(shù)在被調(diào)用前后保持程序的狀態(tài)不變。無副作用的函數(shù)不會改變非局部變量的值,不會改變傳入的參數(shù),也沒有I/O操作。”

    逗號脫口而出:“什么狀態(tài)都不變,那這樣的函數(shù)有什么用?”

    冒號不以為奇:“你的這種想法源自根深蒂固的命令式思維。我們曾把命令式程序比作狀態(tài)自動機(jī),其運行過程就是在不斷地修改機(jī)器的狀態(tài)。而函數(shù)式程序則是進(jìn)行表達(dá)式變換,一般不會改變變量的值。其實函數(shù)式并非完全不改變內(nèi)存,只不過改變的是棧內(nèi)存stack)罷了。換言之,無副作用函數(shù)的作用關(guān)鍵在于其估值結(jié)果,按過程式的說法是返回值。”

    逗號如夢初醒。

    問號仍有疑問:“藥物最好沒有副作用,函數(shù)沒有副作用的好處是什么?”

    冒號嘴一咧:“好處太多了。首先,沒有副作用的函數(shù)易于重構(gòu)、調(diào)試和單元測試。其次,代碼有效性與函數(shù)順序無關(guān),方便并發(fā)處理和優(yōu)化處理。舉個簡單的例子,計算兩個函數(shù)的乘積:f(x) * g(y)。由于無副作用,f(x) g(y)的估值過程是獨立的,估值順序也不重要,因此理論上可以對二者并行計算。另外,還可利用惰性計算lazy evaluation):如果算出f(x)為零,那么不用計算g(y)便可知乘積為零了。”

    嘆號忍不住贊嘆:“聽起來真不錯!”

    冒號言猶未盡:“最后,沒有副作用的函數(shù)是引用透明的(referential transparency),即一個表達(dá)式隨時可以用它的值來替換[6],正如數(shù)學(xué)中的函數(shù)一樣,保證了數(shù)學(xué)思維的貫徹和運用。”

    引號自感獲益頗豐:“前面介紹范式時,覺得函數(shù)式最為神秘?,F(xiàn)在總算有了些感性認(rèn)識了。”

    冒號道出緣由:“函數(shù)式編程不僅有許多獨特的概念和方法,還有很深的數(shù)學(xué)背景——λ-演算(λ-Calculus)。如果一開始便傾囊相授,你們必會望而卻步,我豈不是打草驚蛇了?”

    眾人始覺:老冒原來是在誘敵深入啊。

    “盡管函數(shù)式有這么多優(yōu)點,運算能力從理論上比諸過程式也毫不遜色[7],但一直沒有成為主流范式,”冒號話鋒一轉(zhuǎn),“細(xì)究之,至少有兩方面的原因:主觀上,程序員更習(xí)慣機(jī)器風(fēng)格的過程式思維和現(xiàn)實風(fēng)格OOP思維,不容易接納數(shù)學(xué)風(fēng)格的函數(shù)式思維;客觀上,函數(shù)式語言在表現(xiàn)力和運行效率等方面與過程式和OOP語言也有一定的差距。饒是如此,支持它的語言還是越來越多,其簡潔精巧的特性也為越來越多的人所青睞。它的整體應(yīng)用雖然主要集中于數(shù)學(xué)計算、人工智能等領(lǐng)域,但局部應(yīng)用早已遍地開花了。”


     ,插語

    [1] OOAObject-Oriented Architecture),COA(Component-Oriented Architecture)SOA(Service- Oriented Architecture) 、EDAEvent-Driven Architecture)等。

    [2] 如關(guān)系數(shù)據(jù)庫(relational database)、對象導(dǎo)向式數(shù)據(jù)庫(object-oriented database)等。

    [3] 這一步可省略,但出于對代碼的清晰度以及性能、調(diào)試等方面的考慮,最好保留。

    [4] 這類函數(shù)更數(shù)學(xué)化的說法是高階函數(shù)(higher-order function)。

    [5] 沒有副作用的函數(shù)式語言被稱為純函數(shù)式(purely functional),如HaskellSISAL;有副作用的被稱為非純函數(shù)式(impurely functional),如LispML。

    [6] 這是因為函數(shù)的無副作用性保證了相同的輸入一定有相同的輸出。

    [7] λ-演算被證明是圖靈完備的。


      總結(jié)


    • 學(xué)習(xí)知識之表須通過記憶,掌握知識之里須通過練習(xí),滲透知識之根須通過培養(yǎng)。編程范式正是知識之根。
    • 適度地容忍無知也是一種學(xué)習(xí)技巧。
    • “釘子精神”固然可貴,“刨子精神”也不可少。
    • 不同的編程范式代表不同的解決問題的思想和方法。不同的編程語言提倡不同的編程范式,并在語法上給予支持。只有掌握范式,才能更合理、更有效地運用編程語言的語法和語義特征,并能針對不同的問題領(lǐng)域使用不同的編程風(fēng)格,編寫的代碼才會和諧自然、富于美感。
    • 命令式編程需要指定計算的過程,著重微觀的細(xì)節(jié);聲明式編程只需指定計算的原則,著重宏觀的方向。因此二者繁簡有別。
    • 在函數(shù)式編程中,函數(shù)是程序的核心,是頭等公民,一般沒有或很少副作用,同時沒有顯式的內(nèi)存管理。
    • 由于函數(shù)式編程中的函數(shù)與基本數(shù)據(jù)類型平起平坐,故可將代碼作數(shù)據(jù)用,這是程序既簡潔又強(qiáng)大的原因之一?;卣{(diào)機(jī)制采用的正是函數(shù)式風(fēng)格。
    • 無副作用的函數(shù)容易重構(gòu)、調(diào)試和測試,便于并發(fā)和優(yōu)化處理,并能貫徹和運用數(shù)學(xué)思維。
    • 相比過程式和OOP,函數(shù)式編程思想過于數(shù)學(xué)化和抽象化,語言的表現(xiàn)力和運行效率也有所不足。

     “”參考


    [1] Michael Lee ScottProgramming Language PragmaticsSan FranciscoMorgan Kaufmann2000589-620

    [2] Stephen H. KaislerSOFTWARE PARADIGMSNew JerseyWiley,200523-24

    posted on 2008-11-30 22:27 鄭暉 閱讀(3226) 評論(3)  編輯  收藏 所屬分類: 冒號課堂

    評論

    # re: 冒號課堂§4.1:函數(shù)范式 2008-12-01 09:28 i

    good.  回復(fù)  更多評論   

    # re: 冒號課堂§4.1:函數(shù)范式[未登錄] 2008-12-08 15:51 alex

    看得太過癮了。
    在一個浮躁的年代,越來越多“XX天掌握XXX”的書籍,越來越多的HR說“大學(xué)里的課程都沒用”,越來越多的“流行技術(shù)”被掛在嘴邊。
    作為真正思想獨立的人,就不能缺少表面背后的思考。作為一名在校學(xué)生,希望將來能從事更具思考性的工作。這樣的文章,每一個希望積攢“內(nèi)功”的程序員都喜歡。  回復(fù)  更多評論   

    # re: 冒號課堂§4.1:函數(shù)范式 2008-12-08 15:56 鄭暉

    @alex
    多謝你的肯定。作為一名在校學(xué)生,你能有這樣的想法,實在難能可貴。  回復(fù)  更多評論   

    導(dǎo)航

    統(tǒng)計

    公告

    博客搬家:http://blog.zhenghui.org
    《冒號課堂》一書于2009年10月上市,詳情請見
    冒號課堂

    留言簿(17)

    隨筆分類(61)

    隨筆檔案(61)

    文章分類(1)

    文章檔案(1)

    最新隨筆

    積分與排名

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 777亚洲精品乱码久久久久久 | 国产成人免费a在线视频app| 免费av欧美国产在钱| av大片在线无码免费| 亚洲黄色免费在线观看| 67194成手机免费观看| 亚洲黄色免费在线观看| 国产99视频精品免费观看7| 成年网站免费视频A在线双飞| 国产成人无码免费看视频软件| 色窝窝免费一区二区三区| 四虎国产精品免费久久| 国产麻豆剧传媒精品国产免费| 国产无遮挡吃胸膜奶免费看视频| 国产精品成人免费综合| 免费在线不卡视频| 中文字幕精品无码亚洲字| 国产亚洲3p无码一区二区| 亚洲精品免费在线观看| 亚洲成a人片在线观看中文app| 亚洲一区中文字幕在线电影网 | 久久精品国产亚洲AV无码麻豆 | 亚洲日本一区二区三区在线不卡| 亚洲国产综合久久天堂| 亚洲成A人片777777| 91亚洲精品第一综合不卡播放| 亚洲一区二区三区免费视频| 色综合久久精品亚洲国产| xvideos永久免费入口| 日韩视频在线观看免费| 老司机在线免费视频| 免费a级毛片18以上观看精品| 中文字幕亚洲不卡在线亚瑟| 亚洲精品国产成人99久久| 亚洲日韩一区精品射精| 一级特黄录像视频免费| 91成人在线免费视频| 日韩a级毛片免费观看| 亚洲欧洲成人精品香蕉网| 亚洲天堂一区在线| 在线视频亚洲一区|