Haskell 学习笔记 (4)
Eq
类型类可以使用==
和/=
Ord
类型类可以比较大小Show
类型类可以直接转换为字符串Read
类型类可以从字符串转为该类型Bounded
类型类有上下界Enum
类型类可以枚举,对所有值构造器都是空元的数据类型,我们可以让它成为Enum
类型类的成员
data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
deriving (Eq, Ord, Show, Read, Bounded, Enum)
Monday `compare` Tuesday -- LT
Monday < Tuesday -- True
show Wednesday -- "Wednesday"
read "Wednesday" :: Day -- Wednesday
Saturday == Thursday -- False
Sunday /= Saturday -- True
minBound :: Day -- Monday
maxBound :: Day -- Sunday
succ Monday -- Tuesday
pred Saturday -- Friday
[Thursday .. Sunday] -- [Thursday,Friday,Saturday,Sunday]
- 类型别名可以使代码更易理解
- 类型别名也可以有参数
import Data.Map
type AssocList k v = [(k,v)]
type IntMap v = Map Int v
type IntMap' = Map Int
[(1,2),(4,5),(7,9)] :: AssocList Int Int -- [(1,2),(4,5),(7,9)]
Either a b
数据类型
-- Defined in 'Data.Either':
data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show)
:t Right 'a' -- Right 'a' :: Either a Char
:t Left True -- Left True :: Either Bool b
-- An example:
import qualified Data.Map as Map
data LockerState = Taken | Free deriving (Show, Eq)
type ErrorMessage = String
type Code = String
type LockerMap = Map.Map Int (LockerState, Code)
lockerLookup :: Int -> LockerMap -> Either ErrorMessage Code
lockerLookup lockerNumber map = case Map.lookup lockerNumber map of
Nothing -> Left $ "Locker" ++ show lockerNumber ++ " doesn't exist!"
Just (state, code) -> if state /= Taken
then Right code
else Left $ "Locker " ++ show lockerNumber ++ " is already taken!"
lockers :: LockerMap
lockers = Map.fromList
[(100,(Taken,"ZD39I"))
,(101,(Free ,"JAH3I"))
,(105,(Free ,"QOTSA"))
]
lockerLookup 100 lockers -- Left "Locker 100 is already taken!"
lockerLookup 101 lockers -- Right "JAH3I"
lockerLookup 110 lockers -- Left "Locker110 doesn't exist!"
- 类型本身可以作为该类型的字段,即可以创建递归数据结构
[3,4,5,6]
是3:(4:(5:(6:[])))
的语法糖- 只要以特殊字符命名函数,即可自动将其视为中缀函数
- 值构造器本质上也是函数,但是中缀值构造器必须以冒号开头
infixl
,infixr
固定性声明,可以指定运算符的优先级和结合性- 下面是
List
的两个实现
-- Version 1:
data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
3 `Cons` (4 `Cons` (5 `Cons` Empty)) -- Cons 3 (Cons 4 (Cons 5 Empty))
-- Version 2:
infixr 5 :-:
infixr 5 +++
data List a = Empty | a :-: (List a) deriving (Show, Read, Eq, Ord)
(+++) :: List a -> List a -> List a
Empty +++ ys = ys
(x :-: xs) +++ ys = x :-: (xs +++ ys)
let a = 3 :-: 4 :-: 5 :-: Empty
let b = 6 :-: 7 :-: Empty
a -- 3 :-: (4 :-: (5 :-: Empty))
2 :-: a -- 2 :-: (3 :-: (4 :-: (5 :-: Empty)))
a +++ b -- 3 :-: (4 :-: (5 :-: (6 :-: (7 :-: Empty))))
- 二叉树的定义
- 二叉搜索树的插入
- 二叉搜索树的元素查询
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show)
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = Node x EmptyTree EmptyTree
treeInsert x (Node a lchild rchild)
| x == a = Node a lchild rchild
| x < a = Node a (treeInsert x lchild) rchild
| x > a = Node a lchild (treeInsert x rchild)
treeElem :: (Ord a) => a -> Tree a -> Bool
x `treeElem` EmptyTree = False
x `treeElem` (Node a lchild rchild)
| x == a = True
| x < a = x `treeElem` lchild
| x > a = x `treeElem` rchild
let nums = [8,6,4,1,7,3,5]
let numsTree = foldr treeInsert EmptyTree nums
numsTree -- Prettified output:
-- Node 5
-- (Node 3
-- (Node 1 EmptyTree EmptyTree)
-- (Node 4 EmptyTree EmptyTree)
-- )
-- (Node 7
-- (Node 6 EmptyTree EmptyTree)
-- (Node 8 EmptyTree EmptyTree)
-- )
8 `treeElem` numsTree -- True
9 `treeElem` numsTree -- False
- 类型类 的概念类似其他语言中的 接口
- 一个类型类定义了某些行为(如判断相等性、比较顺序、枚举)
- 类型类的行为通过函数的定义或者待实现的函数声明来实现
- 某个类型是一个类型类的实例,就拥有这个类型类约定的行为
Eq
类型类的定义- 类型变量一定要小写
- 最小完备定义
- 将某个类型作为某个类型类的实例
- 子类化
-- Definition of `Eq`:
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
x == y = not (x /= y)
x /= y = not (x == y)
-- Example of instance:
data TrafficLight = Red | Yellow | Green
instance Eq TrafficLight where
Yellow == Yellow = True
Green == Green = True
Red == Red = True
_ == _ = False
instance Show TrafficLight where
show Yellow = "Yellow Light"
show Green = "Green Light"
show Red = "Red Light"
Red == Red -- True
Red == Yellow -- False
Red /= Green -- True
Red `elem` [Red, Yellow] -- True
[Red, Yellow, Green] -- [Red Light,Yellow Light,Green Light]
-- Example of sub-class:
class (Eq a) => Num a where
...
- 作为类型类实例的带参数类型
- GHCi 中可以用
:info
查询实例 - 一个类似其他语言中的隐式类型转换为
bool
的YesNo
类型 []
是一个带参数的类型,而[a]
则是具体的类型id
表示将参数原样返回
instance (Eq m) => Eq (Maybe m) where
Just x == Just y = x == y
Nothing == Nothing = True
_ == _ = False
:info Maybe
data Maybe a = Nothing | Just a -- Defined in ‘Data.Maybe’
instance Eq a => Eq (Maybe a) -- Defined in ‘Data.Maybe’
instance Monad Maybe -- Defined in ‘Data.Maybe’
instance Functor Maybe -- Defined in ‘Data.Maybe’
instance Ord a => Ord (Maybe a) -- Defined in ‘Data.Maybe’
instance Read a => Read (Maybe a) -- Defined in ‘GHC.Read’
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
-- YesNo:
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show)
data TrafficLight = Red | Yellow | Green
class YesNo a where
yesno :: a -> Bool
instance YesNo Int where
yesno 0 = False
yesno _ = True
instance YesNo Bool where
yesno = id
instance YesNo [a] where
yesno [] = False
yesno _ = True
instance YesNo (Maybe a) where
yesno Nothing = False
yesno (Just _) = True
instance YesNo (Tree a) where
yesno EmptyTree = False
yesno _ = True
instance YesNo TrafficLight where
yesno Red = False
yesno _ = True
yesno (0 :: Int) -- False
yesno $ length [] -- False
yesno "hello" -- True
yesno "" -- False
yesno $ Just 0 -- True
yesno Nothing -- False
yesno True -- True
yesno EmptyTree -- False
yesno [0] -- True
yesno [] -- False
yesno Red -- False
:t yesno -- yesno :: YesNo a => a -> Bool
yesnoIf :: (YesNo y) => y -> a -> a -> a
yesnoIf cond yes no = if yesno cond then yes else no
yesnoIf [] "Yeah!" "No!" -- "No!"
yesnoIf [0,1] "Yeah!" "No!" -- "Yeah!"
yesnoIf True "Yeah!" "No!" -- "Yeah!"
yesnoIf (Just 1) "Yeah!" "No!" -- "Yeah!"
Functor
类型类用来表示可以映射的事物Functor
类型类以一个接受类型参数的类型构造器作为类型参数map
是仅仅处理列表的fmap
Maybe
函子Tree
函子Either a
函子:因为Functor
类型类期望的是取单个参数的类型构造器,而Either
有两个参数,所以可以只交给Either
一个参数对它进行部分应用,这样就它就只剩下一个参数了
-- Definition of `Functor`:
class Functor f where
fmap :: (a -> b) -> f a -> f b
-- `map`:
instance Functor [] where
fmap = map
fmap (*2) [1..3] -- [2,4,6]
map (*2) [1..3] -- [2,4,6]
-- `Maybe` functor, Defined in `Data.Maybe`:
instance Functor Maybe where
fmap f (Just x) = Just (f x)
fmap f Nothing = Nothing
fmap (++ " world") (Just "hello") -- Just "hello world"
fmap (++ " world") Nothing -- Nothing
fmap (*2) (Just 200) -- Just 400
fmap (*2) Nothing -- Nothing
-- `Tree` functor:
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show)
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = Node x EmptyTree EmptyTree
treeInsert x (Node a lchild rchild)
| x == a = Node a lchild rchild
| x < a = Node a (treeInsert x lchild) rchild
| x > a = Node a lchild (treeInsert x rchild)
instance Functor Tree where
fmap f EmptyTree = EmptyTree
fmap f (Node x lchild rchild) = Node (f x) (fmap f lchild) (fmap f rchild)
fmap (*2) EmptyTree -- EmptyTree
fmap (*2) (foldr treeInsert EmptyTree [2,3,1])
-- Node 2 EmptyTree (Node 6 (Node 4 EmptyTree EmptyTree) EmptyTree)
-- `Either a` functor, Defined in `Data.Either`
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap f (Left x) = Left x
fmap show (Left 10) -- Left 10
fmap show (Right 1) -- Right "1"
- 每个类型都有
kind
*
表示一个具体的类型* -> *
意味着类型构造器取一个具体类型,返回一个具体类型- 类型构造器也能够柯里化
:k Int -- Int :: *
:k Either -- Either :: * -> * -> *
:k Either String -- Either String :: * -> *
:k Either String Int -- Either String Int :: *