haskell簡明入門(一)


本文的主要內容參考自《Haskell趣學指南》

1. What is Haskell?

    以下內容引用自[Haskell](https://www.haskell.org/)官網: >Haskell是一個先進的,純粹的函數式編程語言。一個典型的聲明式地,靜態類型的代碼如下: ```haskell primes = filterPrime [2..] where filterPrime (p:xs) = p : filterPrime [x | x <- xs, x `mod` p /= 0] ``` Haskell 有如下的特性: > - **靜態類型**(Statically Typed)。Haskell的每一個表達式都有一個在編譯時決定的類型。所有的由函數引用組合起來的類型必須相匹配(match up),否則無法正常編譯。類型不僅僅是形式上的保證,更是用於表達程序結構的語言。 > - **純粹函數式**(Purely functional)。Haskell的每一個函數都是數學意義上的(pure)。即使是有副作用的IO操作也不過是在描述在做什么,同樣由純粹的代碼產生。沒有聲明或者指令,僅僅只有表達式,該表達式不能對變量進行修改(局部變量或者全局變量),或者是獲取像時間、隨機數這樣的狀態。 > - **類型推斷**(Type Inference)。你並不需要在Haskell中顯式地寫出每一種類型,類型將會進行雙向推斷。當然,你也可以選擇自己寫出類型,或者讓編譯為你進行推斷。 > - **並發**(Concurrent)。Haskell可以很容易進行並發編程,這得力於它可以顯式地處理effects。它的王牌編譯器GHC帶有一個高性能的並行的垃圾回收器,還有一個輕量級的並發庫,其中包含了很多的有用的並發函數原型以及抽象接口。 > - **惰性計算**(Lazy)。函數並不會直接計算它們的值。這意味着程序可以在一起組合地非常好,可以通過只寫通常的函數就表達控制結構(if/else)。Haskell代碼的純粹性使得它可以輕松將函數鏈式組合,操作起來非常方便。 > - **包**(Packages)。你可以在public packages server 上找到非常多的活躍的開源Haskell packages。

2. How to use Haskell?

    你可以下載Haskell的GHC編譯器,該編譯器可以解釋也可以編譯Haskell程序。GHC還有一個很有用的交互模式,在終端輸入ghci即可進入交互模式。然后運行命令`:l demo.hs`就可以加載demo.hs中的函數(Haskell程序文件以hs結尾)。如果你對demo.hs做了修改,可以重新運行命令`:l demo.hs`以重新加載其中的函數。這是我們以后實驗學習Haskell的基本流程。

3. Basic Operations

3.1 基本類型

    在終端輸入ghci進行入交互模式。如下是一些基本的運算,幾乎不用解釋就可以看懂。 ```haskell Prelude> 2+15 17 Prelude> 45*90 4050 Prelude> 568-23 545 Prelude> 3/2 1.5 Prelude> (90-23)*8 536 Prelude> 50*(100-9028) -446400 Prelude> True && False False Prelude> True && True True Prelude> False || True True Prelude> not False True Prelude> not (True && True) False Prelude> 5 == 4 False Prelude> 5 == 5 True Prelude> "Hello" == "Hello" True Prelude> 5 /= 4 True Prelude> 5 /= 5 False ```

3.2 基礎函數

#### 3.2.1 Haskell 常用內置函數 - **succ**。succ 函數返回一個數的后繼。比如: ```haskell Prelude> succ 10 11 ``` 注意Haskell中函數的調用參數是使用空格的,這一點和傳統的編譯型語言有很大的不同。 - **pred**。pred函數和succ函數相反,它返回一個數字的前驅。比如: ```haskell Prelude> pred 10 9 ``` - **min**。min函數接收兩個數字參數,返回這兩個數字的最小值。比如: ```haskell Prelude> min 10 3.4 3.4 ``` 如果要比較多個數字的最小值,直接傳遞多個數字作為參數會報錯,比如: ```haskell Prelude> min 1 19 23 :21:1: Non type-variable argument in the constraint: Num (a -> t) (Use FlexibleContexts to permit this) When checking that ‘it’ has the inferred type it :: forall a t. (Num a, Num (a -> t), Ord (a -> t)) => t ``` 我們可以通過多次調用min函數(注意需要加括號)來解決: ```haskell Prelude> min 1 (min 9 23) 1 ``` - **max**。max函數返回兩個數字的最大值。比如: ```haskell Prelude> max 9.9 9.0 9.9 ``` - Haskell中**函數**擁有最高的優先級。下面兩句是等效的: ```haskell Prelude> succ 10 + max 10 20 * 3 71 Prelude> (succ 10) +(max 10 20)*3 71 ``` 注意`succ 9*10`的值是100而不是91,這是因為計算的順序是先計算`succ 9`得到10,然后再計算`10*10=100`,如果我們要計算9*10的后繼,需要寫成`succ (9*10)`的形式。 - **div**。div函數計算一個整數除以另外一個整數的結果,比如: ```haskell Prelude> div 10 3 3 Prelude> div 10 5 2 ``` 需要注意的是,div函數接收的兩個參數必須是整數,第一個是被除數,第二個是除數,如果除不盡則返回商,舍棄余數。傳入數字不能是浮點數,比如下面的形式會報錯: ```haskell Prelude> div 1.2 3 :31:1: No instance for (Fractional a0) arising from a use of ‘it’ The type variable ‘a0’ is ambiguous Note: there are several potential instances: instance Integral a => Fractional (GHC.Real.Ratio a) -- Defined in ‘GHC.Real’ instance Fractional Double -- Defined in ‘GHC.Float’ instance Fractional Float -- Defined in ‘GHC.Float’ In the first argument of ‘print’, namely ‘it’ In a stmt of an interactive GHCi command: print it ``` #### 3.2.2 構建自己的函數 - doubleMe。doubleMe函數的定義如下: ```haskell doubleMe x = x + x -- 將數字變為兩倍 ``` 這個函數非常簡單,就是返回一個數字的兩倍。注意上面函數的定義方式:首先是函數名字,然后是空格分隔的參數,接着是等於號,等於號后面的是函數的實現。要運行這個函數,需要把它寫在一個文件里,比如add.hs,然后輸入ghci進入命令行模式,輸入`:l add.hs`就可以加載函數了。下面是它的調用計算結果: ```haskell Prelude> :l add.hs [1 of 1] Compiling Main ( add.hs, interpreted ) Ok, modules loaded: Main. *Main> doubleMe 2 4 *Main> doubleMe 3 6 ``` - **doubleUs**。該函數接收x,y兩個參數,然后返回這兩個參數之和的兩倍。定義如下: ```haskell doubleUs x y = x*2 + y*2 -- 兩個數字變為兩倍然后相加 ``` 調用計算結果為: ```haskell *Main> doubleUs 1 3 8 *Main> doubleUs 0 2 4 ``` - **useDoubleMe**。該函數功能和`doubleUs`一樣,只不過其調用了`doubleMe`兩次,代碼如下: ```haskell useDoubleMe x y = doubleMe x + doubleMe y -- 調用簡單函數 ``` 對了,忘了說了,haskell中注釋是`--`。 - **doubleSmallNumber**。該函數使用`if else`語句實現的功能是:如果x小於100則返回x;否則返回x的兩倍。代碼如下: ```haskell doubleSmallNumber x = if x < 100 then x else 2*x --如果x小於100返回x,否則返回2*x ``` 調用結果為: ```haskell *Main> doubleSmallNumber 20 20 *Main> doubleSmallNumber 200 400 ``` 需要注意的是Haskell中if和else一定是一起出現的,else不可以省略,而且本質上if和else都是需要返回一個值。Haskell中所有的函數和表達式都需要返回一個結果,if語句就是一個表達式,所以它一定會返回一個結果。 - **doubleSmallNumber'**。doubleSmallNumber'函數(注意函數最后有一個`'`)返回的結果是doubleSmallNumber返回值加1,定義為: ```haskell doubleSmallNumber' x = (if x < 100 then x else 2*x) + 1 -- doubleSmallNumber返回值加1 ``` 我們通常在Haskell中在函數名字最后加一個`'`來表示對某個函數稍微修改之后得到的新函數。上面的函數如果把括號去掉,那么只會在`x>=100`的時候加1了。另外需要注意的是,Haskell的函數名字開頭不能是大寫字母。 - **conanO'Brien**。conanO'Brien是一個沒有參數的函數,這樣的函數也被稱為"定義"或者"名字"。conanO'Brien定義如下: ```haskell conanO'Brien = "It's a-me, Conan O'Brien!" -- 沒有參數的函數 ``` 上面的conanO'Brien函數定義好了之后,`conanO'Brien`就與字符串`"It's a-me, Conan O'Brien!"`等價了,而且字符串的值是不可以修改的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM