<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                [TOC] ## 模式匹配 ![](http://box.kancloud.cn/2015-07-20_55ad099953a77.png) 本章講的就是haskell那套酷酷的語法結構,先從模式匹配開始。模式匹配通過檢查數據的特定結構來檢查其是否匹配,并按模式從中取得數據。 在定義函數時,你可以為不同的模式分別定義函數體,這就讓代碼更加簡潔易讀。你可以匹配一切數據類型---數字,字符,List,元組,等等。我們弄個簡單函數,讓它檢查我們傳給它的數字是不是7。 ~~~ lucky?::?(Integral?a)?=>?a?->?String??? lucky?7?=?"LUCKY?NUMBER?SEVEN!"??? lucky?x?=?"Sorry,?you're?out?of?luck,?pal!"??? ~~~ 在調用`lucky`時,模式會從上至下進行檢查,一旦有匹配,那對應的函數體就被應用了。這個模式中的唯一匹配是參數為7,如果不是7,就轉到下一個模式,它匹配一切數值并將其綁定為x。這個函數完全可以使用if實現,不過我們若要個分辨1到5中的數字,而無視其它數的函數該怎么辦?要是沒有模式匹配的話,那可得好大一棵if-else樹了! ~~~ sayMe?::?(Integral?a)?=>?a?->?String??? sayMe?1?=?"One!"??? sayMe?2?=?"Two!"??? sayMe?3?=?"Three!"??? sayMe?4?=?"Four!"??? sayMe?5?=?"Five!"??? sayMe?x?=?"Not?between?1?and?5"?? ~~~ 注意下,如果我們把最后匹配一切的那個模式挪到最前,它的結果就全都是`"Not between 1 and 5" ?`了。因為它自己匹配了一切數字,不給后面的模式留機會。 記得前面實現的那個階乘函數么?當時是把`n`的階乘定義成了`product [1..n]`。也可以寫出像數學那樣的遞歸實現,先說明0的階乘是1,再說明每個正整數的階乘都是這個數與它前驅(predecessor)對應的階乘的積。如下便是翻譯到haskell的樣子: ~~~ factorial?::?(Integral?a)?=>?a?->?a??? factorial?0?=?1??? factorial?n?=?n?*?factorial?(n?-?1)?? ~~~ 這就是我們定義的第一個遞歸函數。遞歸在haskell中十分重要,我們會在后面深入理解。如果拿一個數(如3)調用factorial函數,這就是接下來的計算步驟:先計算`3*factorial 2`,`factorial 2`等于`2*factorial 1`,也就是`3*(2*(factorial 1))`。`factorial 1`等于`1*factorial 0`,好,得`3*(2*(1*factorial 0))`,遞歸在這里到頭了,嗯---我們在萬能匹配前面有定義,0的階乘是1.于是最終的結果等于`3*(2*(1*1))`。若是把第二個模式放在前面,它就會捕獲包括0在內的一切數字,這一來我們的計算就永遠都不會停止了。這便是為什么說模式的順序是如此重要:它總是優先匹配最符合的那個,最后才是那個萬能的。 模式匹配也會失敗。假如這個函數: ~~~ charName?::?Char?->?String??? charName?'a'?=?"Albert"??? charName?'b'?=?"Broseph"??? charName?'c'?=?"Cecil"?? ~~~ 拿個它沒有考慮到的字符去調用它,你就會看到這個: ~~~ ghci>?charName?'a'??? "Albert"??? ghci>?charName?'b'??? "Broseph"??? ghci>?charName?'h'??? "***?Exception:?tut.hs:(53,0)-(55,21):?Non-exhaustive?patterns?in?function?charName?? ~~~ 它告訴我們說,這個模式不夠全面。因此,在定義模式時,一定要留一個萬能匹配的模式,這樣我們的程序就不會為了不可預料的輸入而崩潰了。 對Tuple同樣可以使用模式匹配。寫個函數,將二維空間中的向量相加該如何?將它們的x項和y項分別相加就是了。如果不了解模式匹配,我們很可能會寫出這樣的代碼: ~~~ addVectors?::?(Num?a)?=>?(a,?a)?->?(a,?a)?->?(a,?a)??? addVectors?a?b?=?(fst?a?+?fst?b,?snd?a?+?snd?b)?? ~~~ 嗯,可以運行。但有更好的方法,上模式匹配: ~~~ addVectors?::?(Num?a)?=>?(a,?a)?->?(a,?a)?->?(a,?a)??? addVectors?(x1,?y1)?(x2,?y2)?=?(x1?+?x2,?y1?+?y2)?? ~~~ there we go!好多了!注意,它已經是個萬能的匹配了。兩個addVector的類型都是`addVectors:: (Num a) => (a,a) -> (a,a) -> (a,a)`,我們就能夠保證,兩個參數都是序對(Pair)了。 fst和snd可以從序對中取出元素。三元組(Tripple)呢?嗯,沒現成的函數,得自己動手: ~~~ first?::?(a,?b,?c)?->?a??? first?(x,?_,?_)?=?x??? second?::?(a,?b,?c)?->?b??? second?(_,?y,?_)?=?y??? third?::?(a,?b,?c)?->?c??? third?(_,?_,?z)?=?z?? ~~~ 這里的_就和List Comprehension中一樣。表示我們不關心這部分的具體內容。 說到List Comprehension,我想起來在List Comprehension中也能用模式匹配: ~~~ ghci>?let?xs?=?[(1,3),?(4,3),?(2,4),?(5,3),?(5,6),?(3,1)]??? ghci>?[a+b?|?(a,b)??xs]??? [4,7,6,8,11,4] ~~~ 一旦模式匹配失敗,它就簡單挪到下個元素。 對list本身也可以使用模式匹配。你可以用`[]`或`:`來匹配它。因為`[1,2,3]`本質就是`1:2:3:[]`的語法糖。你也可以使用前一種形式,像`x:xs`這樣的模式可以將list的頭部綁定為x,尾部綁定為xs。如果這list只有一個元素,那么xs就是一個空list。 > **Note**:x:xs這模式的應用非常廣泛,尤其是遞歸函數。不過它只能匹配長度大于等于1的list。 如果你要把list的前三個元素都綁定到變量中,可以使用類似`x:y:z:xs`這樣的形式。它只能匹配長度大于等于3的list。 我們已經知道了對list做模式匹配的方法,就實現個我們自己的head函數。 ~~~ head'?::?[a]?->?a??? head'?[]?=?error?"Can't?call?head?on?an?empty?list,?dummy!"??? head'?(x:_)?=?x?? ~~~ 看看管不管用: ~~~ ghci>?head'?[4,5,6]??? 4??? ghci>?head'?"Hello"??? 'H'?? ~~~ 漂亮!注意下,你若要綁定多個變量(用_也是如此),我們必須用括號將其括起。同時注意下我們用的這個error函數,它可以生成一個運行時錯誤,用參數中的字符串表示對錯誤的描述。它會直接導致程序崩潰,因此應謹慎使用。可是對一個空list取head真的不靠譜哇。 弄個簡單函數,讓它用非標準的英語給我們展示list的前幾項。 ~~~ tell?::?(Show?a)?=>?[a]?->?String??? tell?[]?=?"The?list?is?empty"??? tell?(x:[])?=?"The?list?has?one?element:?"?++?show?x??? tell?(x:y:[])?=?"The?list?has?two?elements:?"?++?show?x?++?"?and?"?++?show?y??? tell?(x:y:_)?=?"This?list?is?long.?The?first?two?elements?are:?"?++?show?x?++?"?and?"?++?show?y?? ~~~ 這個函數顧及了空list,單元素list,雙元素list以及較長的list,所以這個函數很安全。`(x:[])`與`(x:y:[])`也可以寫作`[x]`和`[x,y]`(有了語法糖,我們不必多加括號)。不過`(x:y:_)`這樣的模式就不行了,因為它匹配的list長度不固定。 我們曾用List Comprehension實現過自己的length函數,現在用模式匹配和遞歸重新實現它: ~~~ length'?::?(Num?b)?=>?[a]?->?b??? length'?[]?=?0??? length'?(_:xs)?=?1?+?length'?xs?? ~~~ 這與先前寫的那個factorial函數很相似。先定義好未知輸入的結果---空list,這也叫作邊界條件。再在第二個模式中將這List分割為頭部和尾部。說,List的長度就是其尾部的長度加1。匹配頭部用的_,因為我們并不關心它的值。同時也應明確,我們顧及了List所有可能的模式:第一個模式匹配空list,第二個匹配任意的非空list。 看下拿`"ham"`調用`length'`會怎樣。首先它會檢查它是否為空List。顯然不是,于是進入下一模式。它匹配了第二個模式,把它分割為頭部和尾部并無視掉頭部的值,得長度就是`1+length' "am"`。ok。以此類推,`"am"`的`length`就是`1+length' "m"`。好,現在我們有了`1+(1+length' "m")`。`length' "m"`即`1+length ""`(也就是`1+length' []`)。根據定義,`length' []`等于`0`。最后得`1+(1+(1+0))`。 再實現`sum`。我們知道空list的和是0,就把它定義為一個模式。我們也知道一個list的和就是頭部加上尾部的和的和。寫下來就成了: ~~~ sum'?::?(Num?a)?=>?[a]?->?a??? sum'?[]?=?0??? sum'?(x:xs)?=?x?+?sum'?xs?? ~~~ 還有個東西叫做as模式,就是將一個名字和@置于模式前,可以在按模式分割什么東西時仍保留對其整體的引用。如這個模式`xs@(x:y:ys)`,它會匹配出與`x:y:ys`對應的東西,同時你也可以方便地通過xs得到整個list,而不必在函數體中重復`x:y:ys`。看下這個quick and dirty的例子: ~~~ capital?::?String?->?String??? capital?""?=?"Empty?string,?whoops!"??? capital?all@(x:xs)?=?"The?first?letter?of?"?++?all?++?"?is?"?++?[x]?? ~~~ ~~~ ghci>?capital?"Dracula"??? "The?first?letter?of?Dracula?is?D"?? ~~~ 我們使用as模式通常就是為了在較大的模式中保留對整體的引用,從而減少重復性的工作。 還有——你不可以在模式匹配中使用`++`。若有個模式是`(xs++ys)`,那么這個List該從什么地方分開呢?不靠譜吧。而`(xs++[x,y,z])`或只一個`(xs++[x])`或許還能說的過去,不過出于list的本質,這樣寫也是不可以的。 ## 注意,門衛! 模式用來檢查一個值是否合適并從中取值,而門衛(guard)則用來檢查一個值的某項屬性是否為真。咋一聽有點像是if語句,實際上也正是如此。不過處理多個條件分支時門衛的可讀性要高些,并且與模式匹配契合的很好。 ![](http://box.kancloud.cn/2015-07-20_55ad099f881fe.png) 在講解它的語法前,我們先看一個用到門衛的函數。它會依據你的BMI值(body mass index,身體質量指數)來不同程度地侮辱你。BMI值即為體重除以身高的平方。如果小于18.5,就是太瘦;如果在18.5到25之間,就是正常;25到30之間,超重;如果超過30,肥胖。這就是那個函數(我們目前暫不為您計算bmi,它只是直接取一個emi值)。 ~~~ bmiTell?::?(RealFloat?a)?=>?a?->?String??? bmiTell?bmi??? ????|?bmi??18.5?=?"You're?underweight,?you?emo,?you!"??? ????|?bmi??25.0?=?"You're?supposedly?normal.?Pffft,?I?bet?you're?ugly!"??? ????|?bmi??30.0?=?"You're?fat!?Lose?some?weight,?fatty!"??? ????|?otherwise???=?"You're?a?whale,?congratulations!"?? ~~~ 門衛由跟在函數名及參數后面的豎線標志,通常他們都是靠右一個縮進排成一列。一個門衛就是一個布爾表達式,如果為真,就使用其對應的函數體。如果為假,就送去見下一個門衛,如之繼續。如果我們用24.3調用這個函數,它就會先檢查它是否小于等于18.5,顯然不是,于是見下一個門衛。24.3小于25.0,因此通過了第二個門衛的檢查,就返回第二個字符串。 在這里則是相當的簡潔,不過不難想象這在命令式語言中又會是怎樣的一棵if-else樹。由于if-else的大樹比較雜亂,若是出現問題會很難發現,門衛對此則十分清楚。 最后的那個門衛往往都是`otherwise`,它的定義就是簡單一個`otherwise = True`,捕獲一切。這與模式很相像,只是模式檢查的是匹配,而它們檢查的是布爾表達式 。如果一個函數的所有門衛都沒有通過(而且沒有提供otherwise作萬能匹配),就轉入下一模式。這便是門衛與模式契合的地方。如果始終沒有找到合適的門衛或模式,就會發生一個錯誤。 當然,門衛可以在含有任意數量參數的函數中使用。省得用戶在使用這函數之前每次都自己計算bmi。我們修改下這個函數,讓它取身高體重為我們計算。 ~~~ bmiTell?::?(RealFloat?a)?=>?a?->?a?->?String??? bmiTell?weight?height??? ????|?weight?/?height?^?2??18.5?=?"You're?underweight,?you?emo,?you!"??? ????|?weight?/?height?^?2??25.0?=?"You're?supposedly?normal.?Pffft,?I?bet?you're?ugly!"??? ????|?weight?/?height?^?2??30.0?=?"You're?fat!?Lose?some?weight,?fatty!"??? ????|?otherwise?????????????????=?"You're?a?whale,?congratulations!"???? ~~~ 看看我胖不胖...... ~~~ ghci>?bmiTell?85?1.90??? "You're?supposedly?normal.?Pffft,?I?bet?you're?ugly!"?? ~~~ Yay!我不胖!不過haskell依然說我很猥瑣...什么道理... 注意下,函數名和參數的后面并沒有=。許多新人容易搞出語法錯誤,就是因為在后面加上了=。 另一個簡單的例子:實現個自己的`max`函數。應該還記得,它是取兩個可比較的值,返回較大的那個。 ~~~ max'?::?(Ord?a)?=>?a?->?a?->?a??? max'?a?b???? ????|?a?>?b?????=?a??? ????|?otherwise?=?b?? ~~~ 門衛也可以堆一行里面。這樣的可讀性會差些,因而是不被鼓勵的。即使是較短的函數也是如此,僅僅出于演示,我們可以這樣重寫max': ~~~ max'?::?(Ord?a)?=>?a?->?a?->?a??? max'?a?b?|?a?>?b?=?a?|?otherwise?=?b?? ~~~ Ugh!一點都不好讀!繼續進發,用門衛實現我們自己的compare函數: ~~~ myCompare?::?(Ord?a)?=>?a?->?a?->?Ordering??? a?`myCompare`?b??? ????|?a?>?b?????=?GT??? ????|?a?==?b????=?EQ??? ????|?otherwise?=?LT?? ~~~ ~~~ ghci>?3?`myCompare`?2??? GT?? ~~~ > **Note**:通過反單引號,我們不僅可以以中綴形式調用函數,也可以在定義函數的時候使用它。有時這樣會更易讀。 ## Where? 前一節中我們寫了這個bmi計算函數: ~~~ bmiTell?::?(RealFloat?a)?=>?a?->?a?->?String??? bmiTell?weight?height??? ????|?weight?/?height?^?2??18.5?=?"You're?underweight,?you?emo,?you!"??? ????|?weight?/?height?^?2??25.0?=?"You're?supposedly?normal.?Pffft,?I?bet?you're?ugly!"??? ????|?weight?/?height?^?2??30.0?=?"You're?fat!?Lose?some?weight,?fatty!"??? ????|?otherwise???????????????????=?"You're?a?whale,?congratulations!" ~~~ 注意,我們重復了3次。我們重復了3次。程序員的字典里不應該有“重復”這個詞。既然發現有重復,那么給它一個名字來代替這三個表達式會更好些。嗯,我們可以這樣修改: ~~~ bmiTell?::?(RealFloat?a)?=>?a?->?a?->?String??? bmiTell?weight?height??? ????|?bmi??18.5?=?"You're?underweight,?you?emo,?you!"??? ????|?bmi??25.0?=?"You're?supposedly?normal.?Pffft,?I?bet?you're?ugly!"??? ????|?bmi??30.0?=?"You're?fat!?Lose?some?weight,?fatty!"??? ????|?otherwise???=?"You're?a?whale,?congratulations!"??? ????where?bmi?=?weight?/?height?^?2 ~~~ 我們的where關鍵字跟在門衛后面(最好是與豎線縮進一致),可以定義多個名字和函數。這些名字對每個門衛都是可見的,這一來就避免了重復。如果我們打算換種方式計算bmi,只需進行一次修改就行了。通過命名,我們提升了代碼的可讀性,并且由于bmi只計算了一次,函數的執行效率也有所提升。我們可以再做下修改: ~~~ bmiTell?::?(RealFloat?a)?=>?a?->?a?->?String??? bmiTell?weight?height??? ????|?bmi??skinny?=?"You're?underweight,?you?emo,?you!"??? ????|?bmi??normal?=?"You're?supposedly?normal.?Pffft,?I?bet?you're?ugly!"??? ????|?bmi??fat????=?"You're?fat!?Lose?some?weight,?fatty!"??? ????|?otherwise?????=?"You're?a?whale,?congratulations!"??? ????where?bmi?=?weight?/?height?^?2??? ??????????skinny?=?18.5??? ??????????normal?=?25.0??? ??????????fat?=?30.0 ~~~ 函數在_where_綁定中定義的名字只對本函數可見,因此我們不必擔心它會污染其他函數的命名空間。注意,其中的名字都是一列垂直排開,如果不這樣規范,haskell就搞不清楚它們在哪個地方了。 _where_綁定不會在多個模式中共享。如果你在一個函數的多個模式中重復用到同一名字,就應該把它置于全局定義之中。 _where_綁定也可以使用**模式匹配**!前面那段代碼可以改成: ~~~ ...??? where?bmi?=?weight?/?height?^?2??? ??????(skinny,?normal,?fat)?=?(18.5,?25.0,?30.0)?? ~~~ 我們再搞個簡單函數,讓它告訴我們姓名的首字母: ~~~ initials?::?String?->?String?->?String??? initials?firstname?lastname?=?[f]?++?".?"?++?[l]?++?"."??? ????where?(f:_)?=?firstname??? ??????????(l:_)?=?lastname?? ~~~ 我們完全按可以在函數的參數上直接使用模式匹配(這樣更短更簡潔),在這里只是為了演示在where語句中同樣可以使用模式匹配: _where_綁定可以定義名字,也可以定義函數。保持健康的編程風格,我們搞個計算一組bmi的函數: ~~~ calcBmis?::?(RealFloat?a)?=>?[(a,?a)]?->?[a]??? calcBmis?xs?=?[bmi?w?h?|?(w,?h)?? ????where?bmi?weight?height?=?weight?/?height?^?2?? ~~~ 這就全了!在這里將`bmi`搞成一個函數,是因為我們不能依據參數直接進行計算,而必須先從傳入函數的list中取出每個序對并計算對應的值。 _where_綁定還可以嵌套。有個已被廣泛接受的理念,就是一個函數應該有幾個輔助函數。而每個輔助函數也可以通過where擁有各自的輔助函數。 ## Let it be let綁定與where綁定很相似。where綁定是在函數底部定義名字,對包括所有門衛在內的整個函數可見。let綁定則是個表達式,允許你在任何位置定義局部變量,而對不同的門衛不可見。正如haskell中所有賦值結構一樣,let綁定也可以使用模式匹配。看下它的實際應用!這是個依據半徑和高度求圓柱體表面積的函數: ~~~ cylinder?::?(RealFloat?a)?=>?a?->?a?->?a??? cylinder?r?h?=?? ????let?sideArea?=?2?*?pi?*?r?*?h??? ????????topArea?=?pi?*?r?^2??? ????in??sideArea?+?2?*?topArea?? ~~~ ![](http://box.kancloud.cn/2015-07-20_55ad09abcec45.png) let的格式為`let [bindings] in [expressions]`。在_let_中綁定的名字僅對in部分可見。_let_里面定義的名字也得對齊到一列。不難看出,這用_where_綁定也可以做到。那么它倆有什么區別呢?看起來無非就是,_let_把綁定放在語句前面而_where_放在后面嘛。 不同之處在于,_let_綁定本身是個表達式,而_where_綁定則是個語法結構。還記得前面我們講if語句時提到它是個表達式,因而可以隨處安放? ~~~ ghci>?[if?5?>?3?then?"Woo"?else?"Boo",?if?'a'?>?'b'?then?"Foo"?else?"Bar"]??? ["Woo",?"Bar"]??? ghci>?4?*?(if?10?>?5?then?10?else?0)?+?2??? 42 ~~~ 用_let_綁定也可以實現: ~~~ ghci>?4?*?(let?a?=?9?in?a?+?1)?+?2??? 42?? ~~~ _let_也可以定義局部函數: ~~~ ghci>?[let?square?x?=?x?*?x?in?(square?5,?square?3,?square?2)]??? [(25,9,4)]?? ~~~ 若要在一行中綁定多個名字,再將它們排成一列顯然是不可以的。不過可以用分號將其分開。 ~~~ ghci>?(let?a?=?100;?b?=?200;?c?=?300?in?a*b*c,?let?foo="Hey?";?bar?=?"there!"?in?foo?++?bar)??? (6000000,"Hey?there!")?? ~~~ 最后那個綁定后面的分號不是必須的,不過加上也沒關系。如我們前面所說,你可以在let綁定中使用模式匹配。這在從Tuple取值之類的操作中很方便。 ~~~ ghci>?(let?(a,b,c)?=?(1,2,3)?in?a+b+c)?*?100??? 600?? ~~~ 你也可以把let綁定放到List Comprehension中。我們重寫下那個計算bmi值的函數,用個let替換掉原先的where。 ~~~ calcBmis?::?(RealFloat?a)?=>?[(a,?a)]?->?[a]??? calcBmis?xs?=?[bmi?|?(w,?h)??xs,?let?bmi?=?w?/?h?^?2] ~~~ List Comprehension中let綁定的樣子和限制條件差不多,只不過它做的不是過濾,而是綁定名字。let中綁定的名字在輸出函數及限制條件中都可見。這一來我們就可以讓我們的函數只返回胖子的bmi值: ~~~ calcBmis?::?(RealFloat?a)?=>?[(a,?a)]?->?[a]??? calcBmis?xs?=?[bmi?|?(w,?h)??xs,?let?bmi?=?w?/?h?^?2,?bmi?>=?25.0] ~~~ 在`(w, h) <- xs`這里無法使用`bmi`這名字,因為它在let綁定的前面。 在List Comprehension中我們忽略了let綁定的in部分,因為名字的可見性已經預先定義好了。不過,把一個_let...in_放到限制條件中也是可以的,這樣名字只對這個限制條件可見。在GHCi中in部分也可以省略,名字的定義就在整個交互中可見。 ~~~ ghci>?let?zoot?x?y?z?=?x?*?y?+?z??? ghci>?zoot?3?9?2??? 29??? ghci>?let?boot?x?y?z?=?x?*?y?+?z?in?boot?3?4?2??? 14??? ghci>?boot??? >:1:0:?Not?in?scope:?`boot' ~~~ 你說既然_let_已經這么好了,還要where干嘛呢?嗯,let是個表達式,定義域限制的相當小,因此不能在多個門衛中使用。一些朋友更喜歡_where_,因為它是跟在函數體后面,把主函數體距離類型聲明近一些會更易讀。 ## case表達式 ![](http://box.kancloud.cn/2015-07-20_55ad09b083f21.png) 有命令式編程(_C, C++, Java, etc_)的經驗的同學一定會有所了解,很多命令式語言都提供了_case_語句。就是取一個變量,按照對變量的判斷選擇對應的代碼塊。其中可能會存在一個萬能匹配以處理未預料的情況。 haskell取了這一概念融合其中。如其名,case表達式就是,嗯,一種表達式。跟_if..else_和_let_一樣的表達式。用它可以對變量的不同情況分別求值,還可以使用模式匹配。Hmm,取一個變量,對它模式匹配,執行對應的代碼塊。好像在哪兒聽過?啊,就是函數定義時參數的模式匹配!好吧,模式匹配本質上不過就是case語句的語法糖而已。這兩段代碼就是完全等價的: ~~~ head'?::?[a]?->?a??? head'?[]?=?error?"No?head?for?empty?lists!"??? head'?(x:_)?=?x?? ~~~ ~~~ head'?::?[a]?->?a??? head'?xs?=?case?xs?of?[]?->?error?"No?head?for?empty?lists!"??? ??????????????????????(x:_)?->?x?? ~~~ 看得出,_case_表達式的語法十分簡單: ~~~ case?expression?of?pattern?->?result??? ???????????????????pattern?->?result??? ???????????????????pattern?->?result??? ???????????????????...?? ~~~ _expression_匹配合適的模式。如料,第一個模式若匹配,就執行第一個代碼塊;否則就交給下一個模式。如果到最后依然沒有匹配的模式,就會產生一個運行時錯誤。 函數參數的模式匹配只能在定義函數時使用,而case表達式可以用在任何地方。例如: ~~~ describeList?::?[a]?->?String??? describeList?xs?=?"The?list?is?"?++?case?xs?of?[]?->?"empty."??? ???????????????????????????????????????????????[x]?->?"a?singleton?list."???? ???????????????????????????????????????????????xs?->?"a?longer?list."?? ~~~ 這在表達式中作模式匹配很方便,由于模式匹配本質上就是case表達式的語法糖,那么寫成這樣也是等價的: ~~~ describeList?::?[a]?->?String??? describeList?xs?=?"The?list?is?"?++?what?xs??? ????where?what?[]?=?"empty."??? ??????????what?[x]?=?"a?singleton?list."??? ??????????what?xs?=?"a?longer?list."?? ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看