Haskell Basic Notes
Platform
apt-get install haskell-Platform
apt-get install ghc-mod
atom plugins : language-haskell autocomplete-haskell ide-haskell haskell-ghc-mod
ghci
:l
: load file.:r
: reload file.:cd
.:edit
:$EDITOR
.:m
: module.:q
: quit.:?
.:k
: kind.:t
: type function.:info
: data/TypeClass.
ghc
runghc *.hs/*.lhs
Unique Mark
:+
: 复数符,2 :+ 3 -> 2+3i
._
: 泛匹配符, 表示不关心此部分具体内容.<-
: 属于符号, 用于 ListRange 中.=>
: 类型约束分隔符
Expression
if 语句也是表达式
doubleSmallNumber' x = (if x > 100 then x else x*2) + 1
Type
基本类型
ghci> :t 'a'
'a' :: Char
ghci> :t True
True :: Bool
ghci> :t "HELLO!"
"HELLO!" :: [Char]
ghci> :t (True, 'a')
(True, 'a') :: (Bool, Char)
Int
有限整数
Integer
无限整数(效率低)
Float
单精度浮点数
Double
双精度浮点数
Bool
True/False
Char
String
Ordering
LT,GT,EQ
Word
Data.Word - unsigned int
Rational
有理数类型,用于高精度数学运算
List
Operator
++
.:
.!!
.>
/<
/==
.
Function
- head List 首元素.
- last List 尾元素.
- init List 除去尾元素的部分.
- tail List 除去首元素的部分.
- length List 长度.
Tips:
fromIntegral (length [1,2,3,4]) + 3.2
- null List BestPractice: 检查一个 List 是否为空
ghci> null [1,2,3]
False
ghci> null []
True
- reverse List 反转
- take num List 返回 List 前 num 个元素组成的 List
ghci> take 3 [5,4,3,2,1]
[5,4,3]
ghci> take 5 [1,2]
[1,2]
ghci> take 0 [6,6,6]
[]
- drop num List 删 除 List 前 num 个元素
ghci> drop 3 [8,4,2,1,5,6]
[1,5,6]
ghci> drop 0 [1,2,3,4]
[1,2,3,4]
ghci> drop 100 [1,2,3,4]
[]
-
maximum List 返回最大元素
-
minimum List 返回最小元素
-
sum List 返回 List 元素和
-
product List 返回 List 元素积
-
elem
elem
List 判断元素存在性
ghci> 4 `elem` [3,4,5,6]
True
ghci> 10 `elem` [3,4,5,6]
False
- cycle List 返回循环无限数组(Haskell 惰性特性)
- repeat Elem 返回循环无限数组(Haskell 惰性特性)
- replicate num Elem 返回循环无限数组
take 10 (cycle [1,2,3]) -> [1,2,3,1,2,3,1,2,3,1]
take 10 (repeat 5) -> [5,5,5,5,5,5,5,5,5,5]
replicate 3 10 -> [10,10,10]
- takeWhile :: (a -> Bool) ->
[a]
->[a]
遇到不符合限制条件的元素便停止遍历 List
ghci> sum (takeWhile (<10000) (filter odd (map (^2) [1..])))
166650
Range
三要素: ,
与 ..
- 上限
- 下限
- 步长(仅可标明一次)
上下限: [1..20]
步长为2: [2,4..20]
步长为13无限List: [13,26..]
take 24 [13,26..]
List Comprehension
由类似集合定义的离散数学定义,来定义复杂的 List:
[expression | filter]
[expression | x <- Range, Predicate(断言/限制条件)]
- Range:
,
分隔多个 Range(一般为 List) - Predicate:
,
分隔多个断言;每个断言均为 Boolean 表达式
ghci> [x*2 | x <- [1..10], x*2 >= 12]
[12,14,16,18,20]
ghci> [ x | x <- [50..100], x `mod` 7 == 3]
[52,59,66,73,80,87,94]
boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]
ghci> boomBangs [7..13]
["BOOM!","BOOM!","BANG!","BANG!"]
- 多个 Range
ghci> [ x*y | x <- [2,5,10], y <- [8,10,11]]
[16,20,22,40,50,55,80,100,110]
- 嵌套 Comprehension
ghci> let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]]
ghci> [ [ x | x <- xs, even x ] | xs <- xxs]
[[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]
Tuple
内部差异性
- 同一 Tuple 里可存放不同 Type 的项
外部差异性
- 数目不同或某项不同的 Tuple 属于不同 Type
- 不可置于同一 List 中
- 不同长度的 Tuple 不可比较(比较符只可用于相同 Type)
Tuple Function
二元组
fst/snd tuple
: 返回首项/尾项.zip List1 List2
: 对应项配对, 组成二元组 List.
ghci> zip [5,3,2,6,2,7,2,5,4,6,6] ["im","a","turtle"]
[(5,"im"),(3,"a"),(2,"turtle")]
ghci> zip [1..] ["apple", "orange", "cherry", "mango"]
[(1,"apple"),(2,"orange"),(3,"cherry"),(4,"mango")]
三元组
first :: (a, b, c) -> a
first (x, _, _) = x
second :: (a, b, c) -> b
second (_, y, _) = y
third :: (a, b, c) -> c
third (_, _, z) = z
泛型
运用 Type 变量 (只可为单字符), 实现泛型参数与多态函数.
借助 TypeClass 可轻松实现多态函数:
ghci> :t head
head :: [a] -> a
-- a 和 b 可为同类型
-- 第一个参数与返回值必须同类型
ghci> :t fst
fst :: (a, b) -> a
-- 所有参数必须同类型,且必须为Num成员
ghci> :t (*)
(*) :: (Num a) => a -> a -> a
函数类型
- 单个参数
removeNonUppercase :: [Char] -> [Char]
removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]
- 多个参数
addThree :: Int -> Int -> Int -> Int
addThree x y z = x + y + z
Pattern Matching
当函数拥有多个函数体(模式)时, 会从上至下进行匹配各模式,一旦匹配则只应用这一函数体.
As Pattern Matching
all@(pattern)
: all 为指向 pattern 整体的引用.
all@(x:y:xs) -- 其中all与(x:y:xs)等价
capital :: String -> String
capital "" = "Empty string, whoops!"
capital all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]
List Pattern Matching
x:xs
x:y:z:xs
.
head' :: [a] -> a
head' [] = error "Can't call head on an empty list, dummy!"
head' (x:_) = x
length' :: (Num b) => [a] -> b
length' [] = 0
length' (_:xs) = 1 + length' xs
sum' :: (Num a) => [a] -> a
sum' [] = 0
sum' (x:xs) = x + sum' xs
Tuple Pattern Matching
- (x, y)
- (x, y, z)
Guard Pattern Matching and Where Binding
子模式匹配: 运用布尔表达式实现判断, 应用对应函数体:
- 关键符号: | 与 where.
- | 分隔函数体.
- where:
- 可见性: 定义只对本模式可见的 (私有) 名字与 (私有) 函数.
- where 定义在最外层, 使得各模式共享 (私有) 名字与 (私有) 函数.
- 名字定义时可使用模式匹配
where (head:_) = firstName
.
bmiTell :: (RealFloat a) => a -> a -> String
bmiTell weight height
| bmi <= skinny = "You're underweight, you emo, you!"
| bmi <= normal = "You're supposedly normal. Pet, I bet you're ugly!"
| bmi <= fat = "You're fat! Lose some weight, fatty!"
| otherwise = "You're a whale, congratulations!"
where bmi = weight / height ^ 2
skinny = 18.5
normal = 25.0
fat = 30.0
Let Binding
类似 where, 绑定对象为表达式/函数:
let bindings
in expressions
let sideArea = 2 * pi * r * h
topArea = pi * r ^2
in sideArea + 2 * topArea
- 可见性: in 作用域, 只对本 guard 可见.
- 可使用模式匹配.
- 可用于 List Range 中.
Case Pattern Matching
- 模式匹配是 case 表达式的特殊情况(语法糖:简化写法).
- 在函数中, 模式匹配只能用于参数定义中, case 表达式可用于其他地方 (let/where 绑定, 普通表达式, guard 语句).
case expression of pattern -> result
pattern -> result
pattern -> result
...
describeList :: [a] -> String
describeList xs = "The list is " ++ case xs of [] -> "empty."
[x] -> "a singleton list."
xs -> "a longer list."
Pattern Matching Best Practice
- 代替 if-else/switch 语句
- 递归算法(将递归基础作为首模式,递归函数体作为尾模式)
- List Range 中亦可使用模式匹配
addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)
addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
Type Class
ghci> :t (==)
(==) :: (Eq a) => a -> a -> Bool
ghci> :t fromIntegral
fromIntegral :: (Integral a, Num b) => a -> b
=> 符号
=> 左部: 类约束(Class Constraint) => 右部: 函数类型(参数/返回值类型),其中参数类型同属 Class
Basic Type Class
ghci> :info typeClassName
Eq
- 功能: 成员类型可判断相等性
- 成员: 大部分基本类型(不包含函数类型)
- 方法: == 与 /= 函数
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
x == y = not (x /= y)
x /= y = not (x == y)
Ord
Ord 成员必为 Eq 成员: class (Eq a) => Ord a where
- 功能: 成员类型可排序
- 成员: 大部分基本类型(不包含函数类型)
- 方法:
<
>
<=
>=
函数- compare 函数 (Ord a) => a -> a -> Ordering
Show
- 功能: 成员类型可用字符串表示
- 成员: 大部分基本类型(不包含函数类型)
- 方法: show 函数 (Show a) => a -> String
Tips: 结合 Read, 可用于字符串与数值之间的转化
Read
- 功能: 可以将字串转为 Read 某成员类型
- 成员: 大部分基本类型(不包含函数类型)
- 方法: read 函数 (Read a) => String -> a
Tips: 结合 Show, 可用于字符串与数值 之间的转化
Enum
- 功能: 连续性(可枚举), 其成员类型可用于Range中
- 成员: () Bool Char Ordering Int Integer Float Double
[Thursday .. Sunday]
ghci> succ Monday
Tuesday
ghci> pred Saturday
Friday
Bounded
- 功能: 成员类型具有上下限
- 方法: minBound/maxBound 函数 (Bounded a) => a 无参多态常量/定义
ghci> minBound :: Day
Monday
ghci> maxBound :: Day
Sunday
Num
- 功能: 成员类型具有数字特征.
- 成员: 实数 整数 (
Int
/Integer
/Float
/Double
). - 方法:
+
/-
/*
/abs
函数. - 实例: 所有数字都是多态常量/定义(可视为函数).
ghci> :t 20
20 :: (Num t) => t
Integral
- 功能: 成员类型具有数字特征
- 成员: 整型 - Int Integer
Floating
- 功能: 成员类型具有数字特征
- 成员: 浮点型 - Float Double
TypeClass | Method Feature |
---|---|
Functor | f a + (a -> b) -> f b |
Applicative | f a + f (a -> b) -> f b |
Monad | m a + (a -> m b) -> m b |
Functor
- 成员: Maybe a, [], Either a, IO
- 成员 kind 必须为
* -> *
- f 一元类型构造符(type constructor)
- 成员 kind 必须为
- 必须遵守准则:
- fmap id = id
- fmap (f . g) F = fmap f (fmap g F)
ghci> :info Functor
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
($) :: a -> f b -> f a
instance Functor [] where
fmap = map
instance Functor Maybe where
fmap f (Just x) = Just (f x)
fmap f Nothing = Nothing
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap f (Left x) = Left x
instance Functor IO where
fmap f action = do
result <- action
return (f result)
Control Applicative
- 成员:
f :: * -> *
一元类型构造符 (Type Constructor). <*>
: 参数为 2 个 functor 实例, 其中一个包含一个函数.
(<$>) :: (Functor f) => (a -> b) -> f a -> f b
f <$> x = fmap f x
- 作用: 可以用单一一个函数操作多个 functor
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
Maybe
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
(Just f) <*> something = fmap f something
Collection []
instance Applicative [] where
pure x = [x]
fs <*> xs = [f x | f <- fs, x <- xs]
IO
instance Applicative IO where
pure = return
a <*> b = do
f <- a
x <- b
return (f x)
ZipList
instance Applicative ZipList where
pure x = ZipList (repeat x)
ZipList fs <*> ZipList xs = ZipList (zipWith (\f x -> f x) fs xs)
Multi Functor
ghci> pure (+) <*> Just 3 <*> Just 5
Just 8
ghci> pure (+) <*> Just 3 <*> Nothing
Nothing
ghci> pure (+) <*> Nothing <*> Just 5
Nothing
ghci> (*) <$> [2,5,10] <*> [8,10,11]
[16,20,22,40,50,55,80,100,110]
myAction :: IO String
myAction = (++) <$> getLine <*> getLine
ghci> getZipList $ max <$> ZipList [1,2,3,4,5,3] <*> ZipList [5,3,1,2]
[5,3,3,4]
高度封装函数
liftA2
, 对两个 applicative 运用二元函数:
liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = f <$> a <*> b
ghci> liftA2 (:) (Just 3) (Just [4])
Just [3,4]
ghci> (:) <$> Just 3 <*> Just [4]
Just [3,4]
Control Monad
- 成员: 类型构造符(type constructor)
class Monad m where
return :: a -> m a
{- bind -}(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
x >> y = x >>= _ -> y
fail :: String -> m a
fail msg = error msg
- 特性: 允许返回值之间具有弹性交互
{- 当出现异常后,之后所有的值都变为Nothing -}
ghci> return (0,0) >>= landRight 2 >>= landLeft 2 >>= landRight 2
Just (2,4)
ghci> return (0,0) >>= landLeft 1 >>= landRight 4
\ >>= landLeft (-1) >>= landRight (-2)
Nothing
Monad Laws:
- return 满足 Left identity:
return x >>= f 等于 f x
- return 满足 right identity:
m >>= return 等于 m
- Associativity: 结合律
(m >>= f) >>= g 等于 m >>= (\x -> f x >>= g)
ghci> return 3 >>= (\x -> Just (x+100000))
Just 100003
ghci> (\x -> Just (x+100000)) 3
Just 100003
ghci> Just "move on up" >>= (\x -> return x)
Just "move on up"
ghci> [1,2,3,4] >>= (\x -> return x)
[1,2,3,4]
ghci> putStrLn "Wah!" >>= (\x -> return x)
Wah!
{-Tips: 利用结合律合并两个 Monadic Function-}
(<=<) :: (Monad m) => (b -> m c) -> (a -> m b) -> (a -> m c)
f <=< g = (\x -> g x >>= f)
ghci> let f x = [x,-x]
ghci> let g x = [x*3,x*2]
ghci> let h = f <=< g
ghci> h 3
[9,-9,6,-6]
Maybe Monad
具有失败可能性的 context 封装,灵活处理异常(返回值为 Nothing)
实现
applyMaybe :: Maybe a -> (a -> Maybe b) -> Maybe b
applyMaybe Nothing f = Nothing
applyMaybe (Just x) f = f x
instance Monad Maybe where
return x = Just x
Nothing >>= f = Nothing
Just x >>= f = f x
fail _ = Nothing
do 表示法
- 在 do expression 中,每一行都是一个 monadic value
- 检查返回值,使用
<-
foo :: Maybe String
foo = do
x <- Just 3
y <- Just "!"
Just (show x ++ y)
foo :: Maybe String
foo = Just 3 >>= (\x ->
Just "!" >>= (\y ->
Just (show x ++ y)))
routine :: Maybe Pole
routine = do
start <- return (0,0)
first <- landLeft 2 start
Nothing
second <- landRight 2 first
landLeft 1 second
List Monad
- non-determinism(不确定性)
ghci> (*) <$> [1,2,3] <*> [10,100,1000]
[10,100,1000,20,200,2000,30,300,3000]
- 实现
instance Monad [] where
return x = [x]
xs >>= f = concat (map f xs)
fail _ = []
- 返回值交互: 下例中 n 与 return (n, ch) 进行交互
- list comprehension 与 do 表示法 均是 >>= 的语法糖
- list comprehension:
<-
与 条件表达式 - do 表示法:
<-
与 guard 函数
ghci> [1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
{- do 表示法 -}
listOfTuples :: [(Int,Char)]
listOfTuples = do
n <- [1,2]
ch <- ['a','b']
return (n,ch)
sevensOnly :: [Int]
sevensOnly = do
x <- [1..50]
guard ('7' `elem` show x)
return x
{- list comprehension -}
ghci> [ (n,ch) | n <- [1,2], ch <- ['a','b'] ]
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
MonadPlus
使 Monad 具有 Monoid 的性质(二元封闭运算)
instance MonadPlus [] where
mzero = []
mplus = (++)
Monad Algorithms
马走日
- 计算出可移动位置
moveKnight :: KnightPos -> [KnightPos]
moveKnight (c,r) = do
(c',r') <- [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1)
,(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)
]
guard (c' `elem` [1..8] && r' `elem` [1..8])
return (c',r')
- 利用 >>= 向后传递多个可交互的位置
in3 start = return start >>= moveKnight >>= moveKnight >>= moveKnight
in3 :: KnightPos -> [KnightPos]
in3 start = do
first <- moveKnight start
second <- moveKnight first
moveKnight second
- 最后完成完整函数: 产生所有三步的可能位置,检查其中一个位置是否在里面
canReachIn3 :: KnightPos -> KnightPos -> Bool
canReachIn3 start end = end `elem` in3 start
Foldable
import qualified Data.Foldable as F
foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m
instance F.Foldable Tree where
foldMap f Empty = mempty
foldMap f (Node x l r) = F.foldMap f l `mappend`
f x `mappend`
F.foldMap f r
Data Monoid
- 成员: 必须为具体类型 (不可是类型构造符 (Type Constructor)).
- 准则 (Monoid Law):
- 结合律
a·(b·c) = (a·b)·c
. - 无需满足
a mappend b == b mappend a
.
- 结合律
class Monoid m where
mempty :: m -- identity
mappend :: m -> m -> m
mconcat :: [m] -> m
mconcat = foldr mappend mempty
- 实例
instance Monoid [a] where
mempty = []
mappend = (++)
newtype Product a = Product { getProduct :: a }
deriving (Eq, Ord, Read, Show, Bounded)
instance Num a => Monoid (Product a) where
mempty = Product 1
Product x `mappend` Product y = Product (x * y)
ghci> getProduct $ Product 3 `mappend` Product 4 `mappend` Product 2
24
newtype Any = Any { getAny :: Bool }
deriving (Eq, Ord, Read, Show, Bounded)
instance Monoid Any where
mempty = Any False
Any x `mappend` Any y = Any (x || y)
ghci> getAny . mconcat . map Any $ [False, False, False, True]
True
newtype All = All { getAll :: Bool }
deriving (Eq, Ord, Read, Show, Bounded)
instance Monoid All where
mempty = All True
All x `mappend` All y = All (x && y)
ghci> getAll . mconcat . map All $ [True, True, False]
False
instance Monoid Ordering where
mempty = EQ
LT `mappend` _ = LT
EQ `mappend` y = y
GT `mappend` _ = GT
-- Tips:
-- mappend 在左边不等于 EQ 的情况下都会回传左边的值。相反地则回传右边的值
-- 可代替多个 if/else 语句
import Data.Monoid
lengthCompare :: String -> String -> Ordering
lengthCompare x y = (length x `compare` length y) `mappend`
(vowels x `compare` vowels y) `mappend`
(x `compare` y)
where vowels = length . filter (`elem` "aeiou")
instance Monoid a => Monoid (Maybe a) where
mempty = Nothing
Nothing `mappend` m = m
m `mappend` Nothing = m
Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)
instance Monoid (First a) where
mempty = First Nothing
First (Just x) `mappend` _ = First (Just x)
First Nothing `mappend` x = x
ghci> getFirst $ First (Just 'a') `mappend` First Nothing
Just 'a'
Custom Type Class
- 创建新类: 可以只有声明没有实现
class ClassName where
defining code
- 创建已有类的实例: 必须实现所有已声明函数
- 作用等同于 deriving(自由度更大)
- 可以重写函数,去除默认函数处理,达到特定目的
- 先创建新类型
data TrafficLight = Red | Yellow | Green
instance Eq TrafficLight where
Red == Red = True
Green == Green = True
Yellow == Yellow = True
_ == _ = False
instance Show TrafficLight where
show Red = "Red light"
show Yellow = "Yellow light"
show Green = "Green light"
- 创建新类和实现实例时,使用 class constraint
- 可达到类似于继承的效果
- 可达到限制类型的效果
class (Eq a) => Num a where
...
instance (Eq m) => Eq (Maybe m) where
Just x == Just y = x == y
Nothing == Nothing = True
_ == _ = False
data
type
data SelfDefinedTypeName =
ValueConstructorName ValueType .. | .. deriving (TypeClass, ..)
- data 范例
data Point = Point Float Float deriving (Show)
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
- 导出 data
module Shapes
( Point(..)
, Shape(..)
) where
- 后构造器 > 前构造器
e.g True > False
data Bool = False | True deriving (Ord)
函数特性
data type 也是函数,若省略参数亦会造成 Curry 化.
e.g map fx list
ghci> map (Circle 10 20) [4,5,6,6]
[Circle 10.0 20.0 4.0,
Circle 10.0 20.0 5.0,
Circle 10.0 20.0 6.0,
Circle 10.0 20.0 6.0
]
- Value Constructor:使用
ValueConstructorName ValueType ..
可构造出一个该类型的定义/名字
ghci > Circle 10 20 30
Circle 10 20 30
记录语法(Record Syntax)
- 定义
data Person = Person { firstName :: String
, lastName :: String
, age :: Int
, height :: Float
, phoneNumber :: String
, flavor :: String
} deriving (Show)
- 使用
ghci> Car {company="Ford", model="Mustang", year=1967}
Car {company = "Ford", model = "Mustang", year = 1967}
类型参数(Type Parameters)
提高代码的复用性
data Car a b c = Car { company :: a
, model :: b
, year :: c
} deriving (Show)
tellCar :: (Show a) => Car String String a -> String
tellCar (Car {company = c, model = m, year = y}) =
"This " ++ c ++ " " ++ m ++ " was made in " ++ show y
Maybe value constructor
data Maybe a = Nothing | Just a
- Just 可实现转化:
Just :: a -> Maybe a
Deriving(派生)
data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
deriving (Eq, Ord, Show, Read, Bounded, Enum)
ghci> Wednesday
Wednesday
ghci> show Wednesday
"Wednesday"
ghci> read "Saturday" :: Day
Saturday
ghci> Saturday == Sunday
False
ghci> Saturday == Saturday
True
ghci> Saturday > Friday
True
ghci> Monday `compare` Wednesday
LT
ghci> minBound :: Day
Monday
ghci> maxBound :: Day
Sunday
ghci> succ Monday
Tuesday
ghci> pred Saturday
Friday
ghci> [Thursday .. Sunday]
[Thursday,Friday,Saturday,Sunday]
ghci> [minBound .. maxBound] :: [Day]
[Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday]
type 定义
为 data 声明别名 - typedef
type String = [Char]
type PhoneNumber = String
type Name = String
type PhoneBook = [(Name,PhoneNumber)]
- type 类型参数: 匹配 data 类型参数
type AssocList k v = [(k,v)]
type IntMap v = Map.Map Int v
type IntMap = Map.Map Int
类型别名,只可以在 Haskell 的类型部分中使用:
- 定义新类型
- 类型声明
- 类型注释(::)
- 禁止: 定义名字/定义 AssocList [(1,2),(4,5),(7,9)]
高级数据结构
栈
type Stack = [Int]
pop :: Stack -> (Int,Stack)
pop (x:xs) = (x,xs)
push :: Int -> Stack -> ((),Stack)
push a xs = ((),a:xs)
链表
data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
data List a = Empty
| Cons { listHead :: a, listTail :: List a} deriving (Show, Read, Eq, Ord)
二叉树
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
singleton :: a -> Tree a
singleton x = Node x EmptyTree EmptyTree
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = singleton x
treeInsert x (Node a left right)
| x == a = Node x left right
| x < a = Node a (treeInsert x left) right
| x > a = Node a left (treeInsert x right)
treeElem :: (Ord a) => a -> Tree a -> Bool
treeElem x EmptyTree = False
treeElem x (Node a left right)
| x == a = True
| x < a = treeElem x left
| x > a = treeElem x right
ghci> let nums = [8,6,4,1,7,3,5]
ghci> let numsTree = foldr treeInsert EmptyTree nums
函数
递归函数
- 边界条件
- 递归基础
- 递归函数体
List 函数
- 边界条件: 空 List
- 递归函数体: x:xs 取出首元素进行一般操作,对尾部进行递归操作.
maximum' :: (Ord a) => [a] -> a
maximum' [] = error "maximum of empty list"
maximum' [x] = x
{-
maximum' (x:xs)
| x > maxTail = x
| otherwise = maxTail
where maxTail = maximum' xs
-}
maximum' (x:xs) = max x (maximum' xs)
replicate' :: (Num i, Ord i) => i -> a -> [a]
replicate' n x
| n <= 0 = []
| otherwise = x:replicate' (n-1) x
take' :: (Num i, Ord i) => i -> [a] -> [a]
take' n _
| n <= 0 = []
take' _ [] = []
take' n (x:xs) = x : take' (n-1) xs
reverse' :: [a] -> [a]
reverse' [] = []
reverse' (x:xs) = reverse' xs ++ [x]
repeat' :: a -> [a]
repeat' x = x:repeat' x
zip' :: [a] -> [b] -> [(a,b)]
zip' _ [] = []
zip' [] _ = []
zip' (x:xs) (y:ys) = (x,y):zip' xs ys
elem' :: (Eq a) => a -> [a] -> Bool
elem' a [] = False
elem' a (x:xs)
| a == x = True
| otherwise = a `elem'` xs
Awesome Quick Sort
quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
let smallerSorted = quicksort [a | a <- xs, a <= x]
biggerSorted = quicksort [a | a <- xs, a > x]
in smallerSorted ++ [x] ++ biggerSorted
高阶函数
Curry 化
当传入不全参数时,会改变函数的类型,返回值从单类型变成函数类型.
- 当传入不全参数时:
- compare 的类型变为 (Ord a) => a -> (a -> Ordering)
compareWithHundred :: (Num a, Ord a) => a -> Ordering
compareWithHundred x = compare 100 x
- 通过给二元中缀函数传递唯一参数:
- 中缀函数类型由 a -> a -> a 转为 a -> a
divideByTen :: (Floating a) => a -> a
divideByTen = (/10)
ghci> :t (/10)
(/10) :: (Fractional a) => a -> a
- 包装函数:
- 传入一个二元函数作为参数,便可实现 zipWithFunc
- 若在定义时便传入一个函数参数,便可实现 Curry 化
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
ghci> zipWith' (+) [4,2,5,6] [2,6,2,3]
[6,8,7,9]
ghci> zipWith' max [6,3,2,1] [7,3,1,5]
[7,3,2,5]
ghci> zipWith' (++) ["foo ","bar ","baz "] ["fighters","hoppers","aldrin"]
["foo fighters","bar hoppers","baz aldrin"]
ghci> zipWith' (*) (replicate 5 2) [1..]
[2,4,6,8,10]
ghci> zipWith' (zipWith' (*)) [[1,2,3],[3,5,6],[2,3,4]] [[3,2,2],[3,4,5],[5,4,3]]
[[3,4,6],[9,20,30],[10,12,12]]
- 通过 Curry 化,还可省略参数
foo a = bar b a ->
foo = bar b
map 函数
映射函数 - List Comprehension 的函数化
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
- 如果 map 传入的函数参数的类型为 a -> a -> a,则返回的 List 类型为
[a -> a]
(f x 传参不完全,造成了 Curry 化).
ghci> let listOfFun = map (*) [0..]
ghci> (listOfFun !! 4) 5
20
ghci> map (+3) [1,5,3,1,6]
[4,8,6,4,9]
ghci> map (++ "!") ["BIFF","BANG","POW"]
["BIFF!","BANG!","POW!"]
ghci> map (replicate 3) [3..6]
[[3,3,3],[4,4,4],[5,5,5],[6,6,6]]
ghci> map (map (^2)) [[1,2],[3,4,5,6],[7,8]]
[[1,4],[9,16,25,36],[49,64]]
ghci> map fst [(1,2),(3,5),(6,3),(2,6),(2,5)]
[1,3,6,2,2]
filter 函数
过滤函数 - Comprehension 的函数化
filter :: (a -> Bool) -> [a] -> [a]
filter _ [] = []
filter p (x:xs)
| p x = x : filter p xs
| otherwise = filter p xs
ghci> filter (>3) [1,5,3,2,1,6,4,3,2,1]
[5,6,4]
ghci> filter (==3) [1,2,3,4,5]
[3]
ghci> filter even [1..10]
[2,4,6,8,10]
ghci> let notNull x = not (null x) in filter notNull [[1,2,3],[],[3,4,5],[2,2],[],[],[]]
[[1,2,3],[3,4,5],[2,2]]
ghci> filter (`elem` ['a'..'z']) "u LaUgH aT mE BeCaUsE I aM diFfeRent"
"uagameasadifeent"
ghci> filter (`elem` ['A'..'Z']) "i lauGh At You BecAuse u r aLL the Same"
"GAYBALLS"
fold 函数与 scan 函数
如图中所示,左折叠时将 acc 视为第 0 个元素;右折叠时将 acc 视为最后一个元素.
- 三要素:
- 二元函数 \acc x -> function 或 \x acc -> function
- 初始累加值
- 待折叠 List
- 工作原理:
- 不断从 List 中取出元素,进行二元函数调用,直至 List 被取空
- 调用参数分别为 新取出元素 x 与 之前 n 次调用后的累加值 acc
- 返回值作为下次调用的累加值 acc
- 左折叠函数
- foldl
\acc x ->
- foldl1: 取 List 首元素作为初始累加值
- foldl
foldl :: (Foldable t) => (b -> a -> b) -> b -> t a -> b
sum' :: (Num a) => [a] -> a
sum' xs = foldl (\acc x -> acc + x) 0 xs
- 右折叠函数
- foldr
\x acc ->
- foldr1: 取 List 尾元素作为初始累加值
- foldr
foldr :: (Foldable t) => (a -> b -> b) -> b -> t a -> b
map' :: (a -> b) -> [a] -> [b]
map' f xs = foldr (\x acc -> f x : acc) [] xs
- 更多范例
maximum' :: (Ord a) => [a] -> a
maximum' = foldr1 (\x acc -> if x > acc then x else acc)
reverse' :: [a] -> [a]
reverse' = foldl (\acc x -> x : acc) []
product' :: (Num a) => [a] -> a
product' = foldr1 (*)
filter' :: (a -> Bool) -> [a] -> [a]
filter' p = foldr (\x acc -> if p x then x : acc else acc) []
head' :: [a] -> a
head' = foldr1 (\x _ -> x)
last' :: [a] -> a
last' = foldl1 (_ x -> x)
- scanl/scanr/scanl1/scanr1 函数会将每次折叠的结果都记录在一个 List 中
ghci> scanl (+) 0 [3,5,2,1]
[0,3,8,10,11]
ghci> scanr (+) 0 [3,5,2,1]
[11,8,3,1,0]
ghci> scanl1 (\acc x -> if x > acc then x else acc) [3,4,5,3,7,9,2,1]
[3,4,5,5,7,9,9,9]
ghci> scanl (flip (:)) [] [3,2,1]
[[],[3],[2,3],[1,2,3]]
- 逆波兰表达式
import Data.List
solveRPN :: String -> Float
solveRPN = head . foldl foldingFunction [] . words
where foldingFunction (x:y:ys) "*" = (x * y):ys
foldingFunction (x:y:ys) "+" = (x + y):ys
foldingFunction (x:y:ys) "-" = (y - x):ys
foldingFunction (x:y:ys) "/" = (y / x):ys
foldingFunction (x:y:ys) "^" = (y ** x):ys
foldingFunction (x:xs) "ln" = log x:xs
foldingFunction xs "sum" = [sum xs]
foldingFunction xs numberString = read numberString:xs