懂一點Haskell(二)

函數(shù)式編程基礎(chǔ)

編程有兩種根本不同的方式,順序式和函數(shù)式。順序式最好的例子是C語言,它依賴于一個特定的模型,比如馮諾依曼模型。寫C語言程序,你得懂一些計算機基礎(chǔ)知識,得自己分配內(nèi)存......你的每一行程序甚至都能找到對應(yīng)的計算機指令。而函數(shù)式則側(cè)重于從數(shù)學(xué)角度分析問題。重點關(guān)注計算,而不是電腦。相比而言,函數(shù)式編程語言的函數(shù)更像數(shù)學(xué)里的函數(shù)(接下來會講Haskell中函數(shù)必須遵循的幾點準(zhǔn)則),而C語言的函數(shù)則沒有這么嚴(yán)格的要求。Haskell是一門純函數(shù)式編程語言。而更多其它語言則是在這兩種方式之間尋求某種折衷。

function

我們從函數(shù)式編程中最基礎(chǔ)的概念函數(shù)開始說起。那么,什么是函數(shù)?Haskell中的函數(shù)來源于數(shù)學(xué)中的函數(shù)概念。在數(shù)學(xué)中,當(dāng)我們定義一個函數(shù) ?(x)=y,意思是有一個函數(shù) ?,它接受一個參數(shù) x, 映射到值 y 。對于函數(shù) ?而言,每一個 x 只能有一個特定的 y 與之對應(yīng)。Haskell的函數(shù)就和數(shù)學(xué)里的函數(shù)一樣!
現(xiàn)在我們定義一個函數(shù)addThree,它接受三個參數(shù)并返回他們的和。

-- 函數(shù)名 addThree,參數(shù) x y z,返回 x+y+z
addThree x y z= x + y + z

如上例,定義一個Haskell函數(shù)就是這么簡單!

Haskell函數(shù)必須遵循的三條守則:

  • 所有的函數(shù)必須接受至少一個參數(shù)
  • 所有的函數(shù)必須返回一個值
  • 無論何時以相同參數(shù)調(diào)用一個函數(shù)時,必須保證相同的輸出。

第三條規(guī)則又叫做 引用透明性(referential transparency) 這里有必要重點說一下引用透明性,Haskell號稱可以寫出零bug的程序正是因為這一特性。因為你每做一次函數(shù)調(diào)用都可以保證得到期待的輸出!而沒有任何副作用(side effect)!
有人就要問了,難道C語言的函數(shù)就不能保證確定的輸出嗎?下面是實現(xiàn)三個數(shù)相加的C語言代碼。

int addThree(int x, int y, int z){
    return x+y+z;
}

就這個代碼而言,當(dāng)然可以保證確定的輸出??墒?,在C語言中你還可以這樣做。

int a = 8;
int change_a(){
    ++a;
}

上面這個C程序不僅沒有參數(shù),而且每次調(diào)用你根本不知道它干了什么!你也不知道它對誰進(jìn)行了什么操作!當(dāng)然,除非你看源代碼??墒俏覀儎?chuàng)建函數(shù)不就是為了用嗎?難道每個函數(shù)都得去查看它的源代碼?全局變量和靜態(tài)變量的存在使程序變得復(fù)雜起來,當(dāng)然,某種程度上也帶來了一定的便利。在Haskell中,就沒有全局變量和靜態(tài)變量這一說。甚至變量都不是真正的”變量“,比如 x=2,變量一旦定義便不能更改其值了,x在這個程序中永遠(yuǎn)都會等于2。你可以把Haskell中的變量理解為“定義”,這樣更貼切些。然而,Haskell允許在GHCi中進(jìn)行變量更改,也算是給大家一個方便。但在.hs文件中是堅決不允許對變量進(jìn)行重新賦值的!

Lambda

lambda function,又叫匿名函數(shù)。即沒有名字的函數(shù)。它是函數(shù)式編程中基礎(chǔ)的概念。以一個例子說明Haskell中匿名函數(shù)的定義方法。

-- \ 后是參數(shù),本例x,-> 后是函數(shù)體即x的映射。
-- 和它等價的函數(shù)是 double x = x * 2
\x -> x * 2

先來使用一下這個匿名函數(shù)。打開ghci:

Prelude> (\x -> x * 2) 6
12
Prelude> double 6
12

兩個函數(shù)完全相同,那么為什么不用有名字的函數(shù)呢?事實上,確實推薦使用有名字的函數(shù)。lambda一般用于只使用一次的函數(shù),因為只使用一次,也就懶得定義函數(shù)了。

first-class function

函數(shù)作為參數(shù)

假如你有一個函數(shù)ifevenInc,如果參數(shù)n為偶數(shù)就給n加一,否則返回它本身。

ifEvenInc n = if even n
              then n + 1
              else n

這時你又想寫另一個函數(shù)ifEvenDouble,如果參數(shù)n為偶數(shù),就翻倍,否則返回它自身。

ifEvenDouble n = if even n
                 then n * 2
                 else n

這兩個函數(shù)除了then后面的部分,其它部分完全相同!說不定以后你還會想寫ifEvenSquare,Haskell推薦的做法是把大函數(shù)分拆成多個小函數(shù)。雖然這個函數(shù)并不大,但可以說明這個思路。我們的做法是把then后面的部分提出來寫成函數(shù)。

inc n = n + 1
double n = n * 2

接下來就進(jìn)入主題了,把函數(shù)作為參數(shù)!把函數(shù)作為參數(shù),我們就可以把上面兩個函數(shù)ifEvenIncifEvenDouble改為一個函數(shù)ifEven,然后把incdouble函數(shù)以參數(shù)方式傳進(jìn)去。

ifEven func n = if even n
                  func n
                  else n
                  

你也可以把匿名函數(shù)當(dāng)參數(shù)用。打開ghci:

Prelude> ifEven (\x -> x+1) 6
7
Prelude> ifEven double 8
16
                         

函數(shù)作為返回值

以下面的例子說明函數(shù)作為返回值的用法及用途。

wuhanOffice name = name ++ ": Box 789 - wuhan, 10013"

shanghaiOffice name = name ++ " Box 456 - shanghai, 89523"

xianOffice name = name ++ " Box 123 - xian, 65535"

假如有以上三個函數(shù),它們分別生成 name 對應(yīng)不同城市的郵寄地址。
它們這樣使用:

*Main> wuhanOffice "Bob"
"Bob: Box 789 - wuhan, 10013"
*Main> xianOffice "Alice"
"Alice Box 123 - xian, 65535"

這幾個函數(shù)功能很簡單,只是為了說明問題?,F(xiàn)在我們知道name在哪個城市,所以可以直接調(diào)用。但如果我們事先不知道name屬于哪個城市,那么該如何選擇調(diào)用wuhanOffice還是xianOffice呢?我們可以再寫一個函數(shù)addressLetter,把城市也作為參數(shù)和name一起傳進(jìn)去,像下面這樣:

addressLetter name city = ...

在函數(shù)體里面進(jìn)行城市的選擇,然后執(zhí)行相關(guān)的操作。而這些“相關(guān)操作”我們之前已經(jīng)定義好了函數(shù),因此可以再寫一個函數(shù),傳入city得到對應(yīng)的函數(shù)。這就是把函數(shù)作為返回值。如下:

getLocationFunction city = case city of
    "wh" -> wuhanOffice
    "sh" -> shanghaiOffice
    "dc" -> dcOffice
    _ -> (\name -> name)

然后我們的addressLetter就可以這樣寫了:

addressLetter name city = locationFunction name
  where locationFunction = getLocationFunction city
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容