Haskell入門(三):函式語法(syntax in functions)
python入門程式設計, 之後用c++學習資料結構,Haskell萌新。
參考教材 :Learn You a Haskell for Great Good (http://learnyouahaskell.com/)
操作環境 :Ubuntu下Linux64位虛擬機器
由於對Haskell中一些詞語的中文翻譯並不瞭解,接下來的內容中重點名詞將有英文和我理解的中文。
Chapter3 部分內容
分支結構一: 樣式匹配(pattern matching)
分支結構一主要針對傳入引數的數值或結構進行分析,對不同的結構採用不同的處理方式。(specify patterns to which some data should conform and to deconstruct the data according to those patterns.)
我個人認為這有點類似c++中的switch...case break...default
先來看最基礎的數值分析。

分支結構一 數值分析
在上面這個例子裡,x起到了通配的作用,接近default。只不過這裡的default需要我們自己書寫。如果函式執行到結束都沒有發現匹配的會報錯。
可以用數值分析或結構分析實現遞迴。 以下是階乘的遞迴實現。

分支結構一實現遞迴
可以對結構進行分析 , 當傳入引數的型別是複合的tuple或list時。

分支結構一 結構分析
上面的tell函式是正確的,而tell'函式不能實現我們想要的功能。其原因是沒有指定型別的x起了通配的作用,使得它下面的語句失效。(等效的c++中每個case後面都有break,因而只要匹配到一個就會停止匹配。)
可以使用類似於a@b c ..的寫法,使得我們在拆分匹配的同時保留對原引數的引用(a是原引數)。

保留對原引數的引用
上面的第一個firstLetter函式是正確的,第二個會報錯。我認為Haskell的自動型別推斷在這裡起了作用。前面(x:xs)的匹配使得haskell判定x是char型別。++函式只能在兩個相同型別的列表中實現。因而最後一項也應該是一個列表。
分支結構二:哨兵(guard)
分支結構二使用哨兵來檢查輸入引數是否滿足特定的表示式,相對於分支結構一有了更好的普適性。
這裡類似於python的if...elif... else語句。
哨兵們以縮進了的 | 開頭,後接條件判斷語句,再接返回值。最後的else在這裡使用otherwise表示。
簡單的哨兵示例:

哨兵示例
where
在上面的例子中,weight / height ^ 2的反覆出現降低了程式碼的可讀性和靈活性。 為了方便起見,我們可以用where開頭,儲存一些中間變數。
需要注意where裡的變數名要有相同的縮排。
(插一點題外,這裡的whale僅僅是調侃。社會對體重有些苛責Emmm)
改進後的哨兵示例:

改進後的哨兵
另外,where中的臨時變數僅僅在它所在的語句中有效。下面例子中的哨兵依然在同一個語句中,但是如果我們像分支結構一一樣,再使用另一個bmiTell,不同的bmiTell領導不同的樣式,因而where僅在與它最近的bmiTell中起效。為了使它能跨越不同的樣式,我們可以另外寫全域性的函式來解決這個問題。
let
提到了where, 我們就不得不提另一個與區域性變量表達式有關的關鍵詞let。
where通常在函式裡出現,它可以跨越哨兵,但是不能跨越樣式。而let可以在任何表示式中出現,它不能跨越哨兵。
let表示式的寫法是:let 新定義變數 in 表示式。這些變數僅僅在表示式中起作用。在閱讀程式碼時,可以先看錶達式再看let。感覺寫的思路與python的list comprehension有點類似。

let語句的寫法
let語句在list comprehension中也有廣泛的應用。where, let的選用視具體情況而定,最終目的都是增強程式碼可讀性。
分支結構三:case語句
從功能上說,case語句與上面提到pattern matching很相似,但是case語句不僅僅能在函式裡,而且能在程式碼的任何地方使用。同樣需要注意縮排,它的寫法是:
case 表示式 of 樣式1 -> 結果1
樣式2->結果2
……

case的使用
... 一個留下來的小問題是,不太清楚case語句能不能在終端使用。(換行報錯)