chapter_4 《Haskell:函数语法》读书笔记核心内容

2025-09-11 00:00    #  

一、 模式匹配 (Pattern Matching)

模式匹配是 Haskell 中一种极其强大的特性,它允许你根据输入数据的具体“形状”或“值”来定义函数的不同行为。

1. 基本用法

通过为同一个函数提供多个定义(称为“函数子句”),Haskell 会按从上到下的顺序检查哪个模式匹配了当前的参数:

 1-- 如果参数是 7,就返回 "LUCKY NUMBER SEVEN!"
 2lucky :: Int -> String
 3lucky 7 = "LUCKY NUMBER SEVEN!"
 4-- 如果参数是其他任何整数(用 x 匹配),就返回 "Sorry, you're out of luck, pal!"
 5lucky x = "Sorry, you're out of luck, pal!"
 6
 7-- 调用:
 8-- ghci> lucky 7
 9-- "LUCKY NUMBER SEVEN!"
10-- ghci> lucky 8
11-- "Sorry, you're out of luck, pal!"

2. 在递归中的应用

模式匹配是实现递归函数的自然方式,特别是用于定义“基本情况”(Base Case):

1factorial :: Int -> Int
2-- 基本情况:0 的阶乘是 1
3factorial 0 = 1
4-- 递归情况:n 的阶乘是 n * (n-1) 的阶乘
5factorial n = n * factorial (n - 1)

3. 匹配元组 (Tuples) 和列表 (Lists)

模式匹配可以“解构”数据结构:

4. “as”模式(As-Patterns)

使用 @ 符号,你可以在解构的同时,保留对整个匹配项的引用:

 1-- xs@(x:y:_)
 2-- xs 会绑定到整个列表
 3-- x 会绑定到第一个元素
 4-- y 会绑定到第二个元素
 5capital :: String -> String
 6capital "" = "Empty string, whoops!"
 7capital all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]
 8
 9-- 调用:
10-- ghci> capital "Dracula"
11-- "The first letter of Dracula is D"

二、 哨兵 (Guards)

如果说模式匹配是根据“形状”进行分支,那么哨兵(Guards)就是根据“布尔条件”(TrueFalse)来进行分支。

 1bmiTell :: Double -> Double -> String
 2bmiTell weight height
 3    | bmi <= 18.5 = "You're underweight, you emo, you!"
 4    | bmi <= 25.0 = "You're supposedly normal."
 5    | bmi <= 30.0 = "You're fat! Lose some weight, fatty!"
 6    | otherwise   = "You're a whale, congratulations!"
 7    where bmi = weight / height ^ 2 -- 'where' 绑定在下面介绍
 8
 9-- `otherwise` 是一个特殊的哨兵,它总是为 True,用于捕捉所有其他情况,
10-- 类似于其他语言中的 'else'。

三、 where 绑定

where 关键字允许你在函数定义的末尾(在所有哨兵之后)定义局部变量或辅助函数。

1bmiTell :: Double -> Double -> String
2bmiTell weight height
3    | bmi <= skinny = "You're underweight, you emo, you!"
4    | bmi <= normal = "You're supposedly normal."
5    | bmi <= fat    = "You're fat! Lose some weight, fatty!"
6    | otherwise     = "You're a whale, congratulations!"
7    where bmi = weight / height ^ 2
8          -- 可以在 where 内部继续定义
9          (skinny, normal, fat) = (18.5, 25.0, 30.0)

四、 let 绑定

let 绑定是另一种创建局部变量的方式,但它是一个表达式,而不是像 where 那样的“块”。

1cylinder :: Double -> Double -> Double
2cylinder r h =
3    let sideArea = 2 * pi * r * h
4        topArea = pi * r ^ 2
5    in sideArea + 2 * topArea

五、 case 表达式

case 表达式是模式匹配的“表达式版本”。它允许你在函数体内的任何地方,根据一个值进行模式匹配。

 1-- 使用 case 重写 head
 2head' :: [a] -> a
 3head' xs = case xs of []    -> error "No head for empty lists!"
 4                      (x:_) -> x
 5
 6-- 描述列表的 case 表达式
 7describeList :: [a] -> String
 8describeList xs = "The list is " ++ case xs of []  -> "empty."
 9                                               [x] -> "a singleton list."
10                                               _   -> "a longer list."