模式匹配 #

一、模式匹配基础 #

1.1 什么是模式匹配 #

模式匹配是Haskell的核心特性,允许根据数据结构的形式匹配值:

haskell
-- 基本模式匹配
isZero :: Int -> Bool
isZero 0 = True
isZero _ = False

1.2 模式匹配位置 #

haskell
-- 函数定义顶部
head' :: [a] -> a
head' (x:_) = x

-- case表达式
describe x = case x of
    0 -> "zero"
    1 -> "one"
    _ -> "other"

-- let绑定中
let (a, b) = (1, 2) in a + b

1.3 匹配顺序 #

haskell
-- 从上到下匹配
-- 第一个匹配的模式会被使用
describe :: Int -> String
describe 0 = "zero"
describe 1 = "one"
describe 2 = "two"
describe _ = "other"

二、构造器模式 #

2.1 数据构造器匹配 #

haskell
-- Maybe类型匹配
maybeToList :: Maybe a -> [a]
maybeToList Nothing  = []
maybeToList (Just x) = [x]

-- Either类型匹配
safeDivide :: Int -> Int -> Either String Int
safeDivide _ 0 = Left "Division by zero"
safeDivide x y = Right (x `div` y)

2.2 自定义类型匹配 #

haskell
-- 定义数据类型
data Shape = Circle Double | Rectangle Double Double

-- 面积计算
area :: Shape -> Double
area (Circle r)     = pi * r * r
area (Rectangle w h) = w * h

-- 周长计算
perimeter :: Shape -> Double
perimeter (Circle r)     = 2 * pi * r
perimeter (Rectangle w h) = 2 * (w + h)

2.3 嵌套构造器 #

haskell
-- 嵌套Maybe
fromMaybe' :: a -> Maybe a -> a
fromMaybe' defaultVal Nothing  = defaultVal
fromMaybe' _ (Just x)          = x

-- 嵌套列表
deepHead :: [[a]] -> a
deepHead ((x:_):_) = x
deepHead _        = error "empty"

三、字面值模式 #

3.1 数值模式 #

haskell
-- 精确数值匹配
fibonacci :: Int -> Int
fibonacci 0 = 0
fibonacci 1 = 1
fibonacci n = fibonacci (n-1) + fibonacci (n-2)

-- 范围模式(GHC扩展)
{-# LANGUAGE PatternSynonyms #-}

-- 数值守卫
classify :: Int -> String
classify n
    | n < 0     = "negative"
    | n == 0    = "zero"
    | n > 0     = "positive"

3.2 字符和字符串模式 #

haskell
-- 字符匹配
isVowel :: Char -> Bool
isVowel 'a' = True
isVowel 'e' = True
isVowel 'i' = True
isVowel 'o' = True
isVowel 'u' = True
isVowel _  = False

-- 字符串模式
prefixMatch :: String -> String
prefixMatch ('h':'t':'t':'p':rest) = "HTTP: " ++ rest
prefixMatch ('f':'t':'p':rest)    = "FTP: " ++ rest
prefixMatch s                      = "Other: " ++ s

3.3 布尔模式 #

haskell
-- Bool匹配
not' :: Bool -> Bool
not' True  = False
not' False = True

and' :: Bool -> Bool -> Bool
and' True True  = True
and' _    _     = False

or' :: Bool -> Bool -> Bool
or' False False = False
or' _     _      = True

四、通配符模式 #

4.1 下划线通配符 #

haskell
-- 不关心的值用下划线表示
head'' :: [a] -> a
head'' (x:_) = x

-- 多个通配符
snd' :: (a, b) -> b
snd' (_, y) = y

-- 始终匹配
const' :: a -> b -> a
const' x _ = x

4.2 避免警告 #

haskell
-- 不使用参数时用通配符避免警告
printDouble :: Int -> IO ()
printDouble n = putStrLn (show (n * 2))

-- 更好的做法
printDouble' :: Int -> IO ()
printDouble' n = print (n * 2)

4.3 嵌套通配符 #

haskell
-- 元组中的嵌套通配符
third :: (a, b, c) -> c
third (_, _, z) = z

-- 列表中的嵌套通配符
second :: [a] -> Maybe a
second (_:x:_) = Just x
second _       = Nothing

五、As模式 #

5.1 基本语法 #

使用 @ 绑定整个值同时解构:

haskell
-- 保持整个值
firstChar :: String -> Maybe (Char, String)
firstChar s@(x:_) = Just (x, s)
firstChar []       = Nothing

-- 使用
-- firstChar "hello" = Just ('h', "hello")

5.2 避免重复计算 #

haskell
-- 避免重复计算
stripPrefix :: Eq a => [a] -> [a] -> Maybe [a]
stripPrefix prefix whole@(x:xs)
    | prefix `isPrefixOf` whole = Just (drop (length prefix) whole)
    | otherwise                 = Nothing

-- 避免匹配后再次构建
sortAndNub :: Ord a => [a] -> [a]
sortAndNub xs@(h:t)
    | null xs   = []
    | otherwise = h : sortAndNub (filter (>= h) t)
  where
    smaller = filter (< h) xs
    larger  = filter (> h) xs

5.3 复杂As模式 #

haskell
-- 多层As模式
processPair :: (Int, Int) -> String
processPair pair@(a, b)
    | a == b    = "equal: " ++ show pair
    | a < b     = "first smaller: " ++ show pair
    | otherwise = "first larger: " ++ show pair

-- 列表As模式
nonEmptyInit :: [a] -> Maybe [a]
nonEmptyInit all@(x:xs)
    | null xs   = Nothing
    | otherwise = Just (init all)

六、列表模式 #

6.1 构造器模式 #

haskell
-- head和tail
head''' :: [a] -> a
head''' (x:_) = x

tail' :: [a] -> [a]
tail' (_:xs) = xs

-- 初始化和最后一个
init' :: [a] -> [a]
init' [_]    = []
init' (x:xs) = x : init' xs

last' :: [a] -> a
last' [x]    = x
last' (_:xs) = last' xs

6.2 固定长度模式 #

haskell
-- 精确长度
secondElement :: [a] -> Maybe a
secondElement (_:x:_) = Just x
secondElement _       = Nothing

-- 三元组
swapThird :: (a, b, c) -> (c, b, a)
swapThird (x, y, z) = (z, y, x)

6.3 范围模式 #

haskell
-- 列表范围
take' :: Int -> [a] -> [a]
take' n _  | n <= 0 = []
take' _ []           = []
take' n (x:xs)       = x : take' (n-1) xs

-- 使用范围创建
rangePattern :: [Int]
rangePattern = [1..5]  -- [1,2,3,4,5]

6.4 列表推导式中的模式 #

haskell
-- 生成器中的模式
pairs :: [(Int, Int)]
pairs = [(x, y) | x <- [1, 2], y <- [3, 4]]
-- [(1,3),(1,4),(2,3),(2,4)]

-- 带条件的模式
nonEmptyPairs :: [[a]] -> [(a, a)]
nonEmptyPairs xss = [(x, y) | x:xs <- xss, y:yss <- xss, x /= y]

七、元组模式 #

7.1 基本元组匹配 #

haskell
-- 二元组
fst''' :: (a, b) -> a
fst''' (x, _) = x

snd''' :: (a, b) -> b
snd''' (_, y) = y

-- 交换
swap' :: (a, b) -> (b, a)
swap' (x, y) = (y, x)

7.2 嵌套元组 #

haskell
-- 嵌套匹配
associate :: ((a, b), c) -> (a, b, c)
associate ((x, y), z) = (x, y, z)

-- 解构复杂结构
process :: ((Int, Int), String) -> Int
process ((a, b), s) = a + b + length s

7.3 元组函数 #

haskell
-- zip使用模式
zip' :: [a] -> [b] -> [(a, b)]
zip' []     _      = []
zip' _      []     = []
zip' (x:xs) (y:ys) = (x, y) : zip' xs ys

-- unzip
unzip' :: [(a, b)] -> ([a], [b])
unzip' []         = ([], [])
unzip' ((x, y):ps) = (x:xs, y:ys)
  where
    (xs, ys) = unzip' ps

八、记录模式 #

8.1 记录构造器匹配 #

haskell
-- 定义记录
data Person = Person
    { name :: String
    , age  :: Int
    } deriving (Show)

-- 模式匹配记录
getName :: Person -> String
getName (Person n _) = n

getAge :: Person -> Int
getAge (Person _ a) = a

8.2 使用…语法 #

haskell
-- 匹配同时保留原始值
showPerson :: Person -> String
showPerson p@(Person n a) = 
    n ++ " is " ++ show a ++ " years old"

-- 创建并匹配
createAndShow :: String -> Int -> String
createAndShow n a = showPerson (Person n a)

8.3 嵌套记录 #

haskell
-- 嵌套记录类型
data Company = Company
    { companyName :: String
    , address     :: Address
    } deriving (Show)

data Address = Address
    { street :: String
    , city  :: String
    } deriving (Show)

-- 嵌套匹配
getCity :: Company -> String
getCity (Company _ (Address _ c)) = c

九、实践示例 #

9.1 树结构处理 #

haskell
-- 二叉树定义
data Tree a = Leaf | Node a (Tree a) (Tree a)
    deriving (Show)

-- 计算深度
treeDepth :: Tree a -> Int
treeDepth Leaf         = 0
treeDepth (Node _ l r) = 1 + max (treeDepth l) (treeDepth r)

-- 前序遍历
preorder :: Tree a -> [a]
preorder Leaf         = []
preorder (Node x l r) = x : preorder l ++ preorder r

-- 中序遍历
inorder :: Tree a -> [a]
inorder Leaf         = []
inorder (Node x l r) = inorder l ++ [x] ++ inorder r

-- 后序遍历
postorder :: Tree a -> [a]
postorder Leaf         = []
postorder (Node x l r) = postorder l ++ postorder r ++ [x]

9.2 表达式求值 #

haskell
-- 简单表达式类型
data Expr = Lit Int
          | Add Expr Expr
          | Mul Expr Expr
          | Sub Expr Expr
    deriving (Show)

-- 求值
eval :: Expr -> Int
eval (Lit n)       = n
eval (Add e1 e2)   = eval e1 + eval e2
eval (Mul e1 e2)   = eval e1 * eval e2
eval (Sub e1 e2)   = eval e1 - eval e2

-- 化简
simplify :: Expr -> Expr
simplify (Add (Lit 0) e) = simplify e
simplify (Add e (Lit 0)) = simplify e
simplify (Mul (Lit 1) e) = simplify e
simplify (Mul e (Lit 1)) = simplify e
simplify (Mul (Lit 0) _) = Lit 0
simplify (Mul _ (Lit 0)) = Lit 0
simplify e = e

9.3 颜色处理 #

haskell
-- 颜色类型
data Color = Red | Green | Blue | RGB Int Int Int

-- 模式匹配处理
colorToRGB :: Color -> (Int, Int, Int)
colorToRGB Red       = (255, 0, 0)
colorToRGB Green     = (0, 255, 0)
colorToRGB Blue      = (0, 0, 255)
colorToRGB (RGB r g b) = (r, g, b)

isBright :: Color -> Bool
isBright (RGB r g b) = r > 128 || g > 128 || b > 128
isBright _           = False

十、总结 #

模式匹配要点:

  1. 基本语法:在函数定义中使用模式
  2. 构造器匹配:匹配数据类型的构造器
  3. 字面值匹配:匹配特定值
  4. 通配符:用 _ 忽略不关心的值
  5. As模式:用 @ 保留整个值
  6. 列表模式:使用 : 匹配列表结构
  7. 元组模式:匹配元组结构
  8. 记录模式:使用记录字段匹配

掌握模式匹配后,让我们继续学习守卫表达式。

最后更新:2026-03-27