<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>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## 相信類型 ![](http://box.kancloud.cn/2015-07-20_55acc8122ed5e.png) 在前面我們談到Haskell是靜態類型的,在編譯時每個表達式的類型都已明確,這就提高了代碼的安全性。若代碼中讓布爾值與數字相除,就不會通過編譯。這樣的好處就是與其讓程序在運行時崩潰,不如在編譯時捕獲可能的錯誤。Haskell中萬物皆有類型,因此在執行編譯之時編譯器可以大有所為。 與java和pascal不同,haskell支持類型推導。寫下一個數字,你就沒必要另告訴haskell說“它是個數字”,它自己能推導出來。這樣我們就不必在每個函數或表達式上都標明其類型了。在前面我們只簡單涉及一下haskell的類型方面的知識,但是理解這一類型系統對于haskell 的學習是至關重要的。 類型是每個表達式都有的某種標簽,它標明了這一表達式所屬的范疇。例如,表達式True是boolean型,"hello"是個字符串,等等。 可以使用ghci來檢測表達式的類型。使用:t命令后跟任何可用的表達式,即可得到該表達式的類型,先試一下: ~~~ ghci>?:t?'a'??? 'a'?::?Char??? ghci>?:t?True??? True?::?Bool??? ghci>?:t?"HELLO!"??? "HELLO!"?::?[Char]??? ghci>?:t?(True,?'a')??? (True,?'a')?::?(Bool,?Char)??? ghci>?:t?4?==?5??? 4?==?5?::?Bool ~~~ ![](http://box.kancloud.cn/2015-07-20_55acc81554eaf.png) 可以看出,`:t`命令處理一個表達式的輸出結果為表達式后跟`::`及其類型,`::`讀作“它的類型為”。凡是明確的類型,其首字母必為大寫。`'a'`,如它的樣子,是`Char`類型,易知是個字符(character)。`True`是`Bool`類型,也靠譜。不過這又是啥,檢測`"hello"`得一個`[Char]`?這方括號表示一個List,所以我們可以將其讀作“一組字符的List”。而與List不同,每個Tuple都是獨立的類型,于是`(True,"a")`的類型是`(Bool,Char)`,而`('a','b','c')`的類型為`(Char,Char,Char)`。`4==5`一定返回 False,所以它的類型為Bool。 同樣,函數也有類型。編寫函數時,給它一個明確的類型聲明是個好習慣,比較短的函數就不用多此一舉了。還記得前面那個過濾大寫字母的List Comprehension嗎?給它加上類型聲明便是這個樣子: ~~~ removeNonUppercase?::?[Char]?->?[Char]??? removeNonUppercase?st?=?[?c?|?c??st,?c?`elem`?['A'..'Z']] ~~~ `removeNonUppercase`的類型為`[Char]->[Char]`,從它的參數和返回值的類型上可以看出,它將一個字符串映射為另一個字符串。`[Char]`與`String`是等價的,但使用String會更清晰:`removeNonUppercase :: String -> String`。編譯器會自動檢測出它的類型,我們還是標明了它的類型聲明。要是多個參數的函數該怎樣?如下便是一個將三個整數相加的簡單函數。 ~~~ addThree?::?Int?->?Int?->?Int?->?Int??? addThree?x?y?z?=?x?+?y?+?z ~~~ 參數之間由->分隔,而與返回值之間并無特殊差異。返回值是最后一項,參數就是前三項。稍后,我們將講解為何只用->而不是`Int,Int,Int->Int`之類“更好看”的方式來分隔參數。 如果你打算給你編寫的函數加上個類型聲明卻拿不準它的類型是啥,只要先不寫類型聲明,把函數體寫出來,再使用:t命令測一下即可。函數也是表達式,所以:t對函數也是同樣可用的。 如下是幾個常見的類型: **Int**表示整數。7可以是Int,但7.2不可以。Int是有界的,也就是說它由上限和下限。對32位的機器而言,上限一般是214748364,下限是-214748364。 **Integer**表示...厄...也是整數,但它是無界的。這就意味著可以用它存放非常非常大的數,我是說非常大。它的效率不如Int高。 ~~~ factorial?::?Integer?->?Integer??? factorial?n?=?product?[1..n] ~~~ ~~~ ghci>?factorial?50??? 30414093201713378043612608166064768844377641568960512000000000000 ~~~ **Float**表示單精度的浮點數。 ~~~ circumference?::?Float?->?Float??? circumference?r?=?2?*?pi?*?r ~~~ ghci>?circumference?4.0??? 25.132742 **Double**表示雙精度的浮點數。 ~~~ circumference'?::?Double?->?Double??? circumference'?r?=?2?*?pi?*?r ghci>?circumference'?4.0??? 25.132741228718345 ~~~ **Bool**表示布爾值,它只有兩種值:True和False。 **Char**表示一個字符。一個字符由單引號括起,一組字符的List即為字符串。 Tuple的類型取決于它的長度及其中項的類型。注意,空Tuple同樣也是個類型,它只有一種值:`()`。 ## 類型變量 你覺得head函數的類型是啥?它可以取任意類型的List的首項,是怎么做到的呢?我們查一下! ~~~ ghci>?:t?head??? head?::?[a]?->?a ~~~ ![](http://box.kancloud.cn/2015-07-20_55acc8174181f.png) 嗯! a是啥?類型嗎?想想我們在前面說過,凡是類型其首字母必大寫,所以它不會是個類型。它是個類型變量,意味著a可以是任意的類型。這一點與其他語言中的泛型(generic)很相似,但在haskell中要更為強大。它可以讓我們輕而易舉地寫出類型無關的函數。使用到類型變量的函數被稱作“多態函數 ”,head函數的類型聲明里標明了它可以取任意類型的List并返回其中的第一個元素。 在命名上,類型變量使用多個字符是合法的,不過約定俗成,通常都是使用單個字符,如a,b,c,d... 還記得fst?我們查一下它的類型: ~~~ ghci>?:t?fst??? fst?::?(a,?b)?->?a ~~~ 可以看到fst取一個包含兩個類型的Tuple作參數,并以第一個項的類型作為返回值。這便是fst可以處理一個含有兩種類型項的pair的原因。注意,a和b是不同的類型變量,但它們不一定非得是不同的類型,它只是標明了首項的類型與返回值的類型相同。 ## 類型類101 ![](http://box.kancloud.cn/2015-07-20_55acc81a2db14.png) 類型定義行為的接口,如果一個類型屬于某類型類,那它必實現了該類型類所描述的行為。很多從OOP走過來的人們往往會把類型類當成面向對象語言中的類而感到疑惑,厄,它們不是一回事。易于理解起見,你可以把它看做是java中接口(interface)的類似物。 ==函數的類型聲明是怎樣的? ~~~ ghci>?:t?(==)??? (==)?::?(Eq?a)?=>?a?->?a?->?Bool ~~~ > **Note**:判斷相等的==運算符是函數,+-*/之類的運算符也是同樣。在默認條件下,它們多為中綴函數。若要檢查它的類型,就必須得用括號括起使之作為另一個函數,或者說以前綴函數的形式調用它。 有意思。在這里我們見到個新東西:=>符號。它左邊的部分叫做類型約束。我們可以這樣閱讀這段類型聲明:“相等函數取兩個相同類型的值作為參數并返回一個布爾值,而這兩個參數的類型同在Eq類之中(即類型約束)” **Eq**這一類型類提供了判斷相等性的接口,凡是可比較相等性的類型必屬于Eq類。 ~~~ ghci>?5?==?5???? True???? ghci>?5?/=?5???? False???? ghci>?'a'?==?'a'???? True???? ghci>?"Ho?Ho"?==?"Ho?Ho"???? True???? ghci>?3.432?==?3.432???? True ~~~ elem函數的類型為:`(Eq a)=>a->[a]->Bool`。這是它在檢測值是否存在于一個list時使用到了==的緣故。 幾個基本的類型類: **Eq**包含可判斷相等性的類型。提供實現的函數是==和/=。所以,只要一個函數有Eq類的類型限制,那么它就必定在定義中用到了==和/=。剛才說了,除函數意外的所有類型都屬于Eq,所以它們都可以判斷相等性。 **Ord**包含可比較大小的類型。除了函數以外,我們目前所談到的所有類型都屬于Ord類。Ord包中包含了,=之類用于比較大小的函數。compare函數取兩個Ord類中的相同類型的值作參數,返回比較的結果。這個結果是如下三種類型之一:GT,LT,EQ。 ~~~ ghci>?:t?(>)??? (>)?::?(Ord?a)?=>?a?->?a?->?Bool ~~~ 類型若要成為Ord的成員,必先加入Eq家族。 ~~~ ghci> "Abrakadabra" < "Zebra" True ghci> "Abrakadabra" `compare` "Zebra" LT ghci> 5 >= 2 True ghci> 5 `compare` 3 GT ~~~ **Show**的成員為可用字符串表示的類型。目前為止,除函數以外的所有類型都是Show的成員。操作Show類型類,最常用的函數表示show。它可以取任一Show的成員類型并將其轉為字符串。 ~~~ ghci>?show?3??? "3"??? ghci>?show?5.334??? "5.334"??? ghci>?show?True??? "True" ~~~ **Read**是與Show相反的類型類。read函數可以將一個字符串轉為Read的某成員類型。 ~~~ ghci>?read?"True"?||?False??? True??? ghci>?read?"8.2"?+?3.8??? 12.0??? ghci>?read?"5"?-?2??? 3??? ghci>?read?"[1,2,3,4]"?++?[3]??? [1,2,3,4,3] ~~~ 一切良好,如上的所有類型都屬于這一類型類。嘗試read "4"又會怎樣? ~~~ ghci> read "4" < interactive >:1:0: Ambiguous type variable `a' in the constraint: `Read a' arising from a use of `read' at :1:0-7 Probable fix: add a type signature that fixes these type variable(s) ~~~ ghci跟我們說它搞不清楚我們想要的是什么樣的返回值。注意調用read后跟的那部分,ghci通過它來辨認其類型。若要一個boolean值,他就 知道必須得返回一個Bool類型的值。但在這里它只知道我們要的類型屬于Read類型類,而不能明確到底是哪個。看一下read函數的類型聲明吧: ~~~ ghci>?:t?read??? read?::?(Read?a)?=>?String?->?a ~~~ 看?它的返回值屬于Read類型類,但我們若用不到這個值,它就永遠都不會得知該表達式的類型。所以我們需要在一個表達式后跟`::`的**類型注釋**,以明確其類型。如下: ~~~ ghci>?read?"5"?::?Int??? 5??? ghci>?read?"5"?::?Float??? 5.0??? ghci>?(read?"5"?::?Float)?*?4??? 20.0??? ghci>?read?"[1,2,3,4]"?::?[Int]??? [1,2,3,4]??? ghci>?read?"(3,?'a')"?::?(Int,?Char)??? (3,?'a') ~~~ 編譯器可以辨認出大部分表達式的類型,但遇到`read "5"`的時候它就搞不清楚究竟該是Int還是Float了。只有經過運算,haskell才會明確其類型;同時由于haskell是靜態的,它還必須得在 編譯前搞清楚所有值的類型。所以我們就最好提前給它打聲招呼:“嘿,這個表達式應該是這個類型,省的你認不出來!” **Enum**的成員都是連續的類型--也就是可枚舉。Enum類存在的主要好處就在于我們可以在Range中用到它的成員類型:每個值都有后繼子(successer)和前置子(predecesor),分別可以通過succ函數和pred函數得到。該類型類包含的類型有:`()`,`Bool`,`Char`,`Ordering`,`Int`,`Integer`,`Float`和`Double`。 ~~~ ghci>?['a'..'e']??? "abcde"??? ghci>?[LT?..?GT]??? [LT,EQ,GT]??? ghci>?[3?..?5]??? [3,4,5]??? ghci>?succ?'B'??? 'C' ~~~ **Bounded**的成員都有一個上限和下限。 ~~~ ghci>?minBound?::?Int??? -2147483648??? ghci>?maxBound?::?Char??? '\1114111'??? ghci>?maxBound?::?Bool??? True??? ghci>?minBound?::?Bool??? False ~~~ `minBound`和`maxBound`函數很有趣,它們的類型都是`(Bounded a) => a`。可以說,它們都是多態常量。 如果其中的項都屬于`Bounded`類型類,那么該Tuple也屬于`Bounded` ~~~ ghci>?maxBound?::?(Bool,?Int,?Char)??? (True,2147483647,'\1114111') ~~~ **Num**是表示數字的類型類,它的成員類型都具有數字的特征。檢查一個數字的類型: ~~~ ghci>?:t?20??? 20?::?(Num?t)?=>?t ~~~ 看樣子所有的數字都是多態常量,它可以作為所有`Num`類型類中的成員類型。以上便是`Num`類型類中包含的所有類型,檢測*運算符的類型,可以發現它可以處理一切的數字: ~~~ ghci>?:t?(*)??? (*)?::?(Num?a)?=>?a?->?a?->?a ~~~ 它只取兩個相同類型的參數。所以`(5 :: Int) * (6 :: Integer)`會引發一個類型錯誤,而`5 * (6 :: Integer)`就不會有問題。 類型只有親近`Show`和`Eq`,才可以加入`Num`。 **Integral**同樣是表示數字的類型類。Num包含所有的數字:實數和整數。而Intgral僅包含整數,其中的成員類型有Int和Integer。 **Floating**僅包含浮點類型:Float和Double。 有個函數在處理數字時會非常有用,它便是`fromIntegral`。其類型聲明為:`fromIntegral :: (Num b, Integral a) => a -> b`。從中可以看出,它取一個整數做參數并返回一個更加通用的數字,這在同時處理整數和浮點時會尤為有用。舉例來說,`length`函數的類型聲明為:`length :: [a] -> Int`,而非更通用的形式,如`(Num b) => length :: [a] -> b`。這應該時歷史原因吧,反正我覺得挺蠢。如果取了一個List長度的值再給它加3.2就會報錯,因為這是將浮點數和整數相加。面對這種情況,我們就用`fromIntegral (length [1,2,3,4]) + 3.2`來解決。 注意到,`fromIntegral`的類型聲明中用到了多個類型約束。如你所見,只要將多個類型約束放到括號里用逗號隔開即可。
                  <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>

                              哎呀哎呀视频在线观看