喔呼連續發兩篇文呢><
在這篇文中主要會介紹定義函式時會使用的語法
然後這應該是最後一篇單純介紹語法的Haskell簡介了><
函式定義
在Haskell中定義函式的方法很簡單,例如若要定義一個平方函式,只要square x = x^2
即可。
注意Haskell的函數必須是小寫字母開頭,而大寫字母開頭的東西被留給型別的建構子。
一般而言為了避免一些不必要的問題,在寫函式時會指定他的型別,如
1 | square :: Int -> Int |
注意:要在ghci打入多行程式時,需要在開頭結尾分別用:{
與:}
包住。注意兩個都要各佔一行。
Haskell也支援這種寫法
1 | fib :: Int -> Int |
每次呼叫fib
,Haskell會由上至下看看那一個符合參數的樣子(也就是模式匹配, pattern matching),並執行第一個符合的定義。但是這些定義必須要一次寫完,中間不能空行,更不能在程式碼的另外一個地方把定義補完。
又或是
1 | showSum :: (Int, Int) -> String |
p@(a, b)
代表p
會匹配到整個二元組,而a
與b
會分別匹配到二元組的兩項。
多引數時也是一樣。以下我們來試圖自己實做take
函式,稱為take'
[1]。
1 | -- self-implemented take |
其中_
的意思就是我們不在乎那個參數的值。
而在haskell中的單行註解是以--
開頭,多行註解是以{-
和-}
包住。
運算子
另外,運算子也可以用同樣的方法被定義。若一個函式的名稱僅由符號組成,那它就是一個運算子。例如
1 | (.*.) :: [a] -> [b] -> [(a, b)] |
接著不論是運算子,或是以`function`
定義的中綴函數,都可被定義是左結合或右結合,以及他的優先度(fixity)。優先度由0
至9
,越高者越先被計算。
語法是[infix|infixr|infixl] (fixity) op
,例如infix 3 (.*.)
或是infixl `op`
。
預設的運算子是infixl 9
,而預設的中綴函數是infix 9
。
以上這種定義函數的方法其實是λ函式的語法糖。
λ函式
所謂λ函式就是inline function。
語法是square = \x -> x^2
。或是加上型別是square = (\x -> x^2)::Int -> Int
。
那個\
就是λ的意思[2]。
當然也可以有多變數函數add = \x -> \y -> x + y
。
或是語法糖可以讓它簡化成\x y -> x + y
。
常用語法
let ... in
用法是let x = 3 in x * x
,是(\x -> x * x) 3
的語法糖。
在let ... in
中也可以定義函式。如let square x = x*x in square 3
。
或甚至可以定義多於一個函式[3],如
1 | let y = 3 |
注意那個縮排[4]。
在let ... in
中還可以做模式匹配,但如果無法匹配時就會丟出錯誤。
如let (a, b) = p in a + b
就是把二元組的兩個元素相加。
守衛子句與where
除了以上的寫法,有時我們需要定義一些能橫跨不同定義的函式。這時就可以使用守衛子句的寫法。以atan2
為例:
1 | atan2 :: Float -> Float -> Float |
在這裡otherwise
是為了讓守衛子句比較清楚而在Prelude定義為True
,undefined
是一個當被執行就會丟出錯誤的函式。
if ... then ... else
正常的三元運算子。例如even x = if (x `mod` 2) == 1 then False else True
。
case ... of
當想要在函式的其他地方進行模式匹配使得不同模式會有不同結果時,可以使用case ... of
。 例如
1 | say :: Int -> String |
舉例
讀者可以比較一下以上幾種不同的語法實際寫起來有什麼不同
(在這裡偷一下learn you a haskell的例子):
1 | describeList :: [a] -> String |
1 | describeList :: [a] -> String |
1 | describeList :: [a] -> String |
1 | describeList :: [a] -> String |
問題:你能自己寫出上一篇提到的Prelude裡面包含的各個函式嗎?
前篇解答
問題:請寫出所有三邊長小於100的畢式直角三角形。
1 | [(i, j, k)| i <- [1..100], j <- [1..100], k <- [1..100], i^2+j^2 == k^2] |
問題:什麼是flip const const
?
const a = \b -> a
=> const a b = a
=>
(flip const) b a = a
=> (flip const) const a = a
=>
flip const const = \a -> a = id
問題:什麼又是curry fst
?
fst = \(a, b) -> a
=> curry fst = \a b -> a = const
問題:什麼又是((not.).(null.).filter.(==))
?
就是elem
喔~
後語
這次就先到這裡了,下次開始會進入有趣的內容唷><
敬請期待~
喔然後有錯的話請來敲我>< 八成錯誤百出QQ