守卫表达式 #

一、守卫基础 #

1.1 什么是守卫 #

守卫是一种基于布尔表达式的条件分支机制:

haskell
-- 守卫语法
signum :: Int -> Int
signum x
    | x > 0     = 1
    | x < 0     = -1
    | otherwise = 0

1.2 守卫语法结构 #

haskell
function arg
    | condition1 = result1
    | condition2 = result2
    | otherwise  = defaultResult

1.3 守卫与if-then-else #

haskell
-- 使用守卫
absolute x
    | x >= 0    = x
    | otherwise = -x

-- 等价的if-then-else
absolute' x = if x >= 0 then x else -x

二、多守卫 #

2.1 多条件分支 #

haskell
-- 成绩等级
grade :: Int -> Char
grade score
    | score >= 90 = 'A'
    | score >= 80 = 'B'
    | score >= 70 = 'C'
    | score >= 60 = 'D'
    | otherwise   = 'F'

-- BMI分类
bmiCategory :: Double -> String
bmiCategory bmi
    | bmi < 18.5  = "Underweight"
    | bmi < 25    = "Normal"
    | bmi < 30    = "Overweight"
    | otherwise   = "Obese"

2.2 守卫顺序 #

haskell
-- 守卫按顺序检查
describe :: Int -> String
describe n
    | n < 0     = "negative"
    | n == 0    = "zero"
    | n < 10    = "small positive"
    | n < 100   = "medium positive"
    | otherwise = "large positive"

2.3 otherwise #

haskell
-- otherwise是True的别名
otherwise :: Bool
otherwise = True

-- 使用otherwise作为默认情况
classify :: Int -> String
classify n
    | n < 0     = "negative"
    | n == 0    = "zero"
    | otherwise = "positive"

三、守卫与模式匹配 #

3.1 结合使用 #

haskell
-- 模式匹配后使用守卫
take' :: Int -> [a] -> [a]
take' n _
    | n <= 0    = []
take' _ []      = []
take' n (x:xs)  = x : take' (n-1) xs

-- drop函数
drop' :: Int -> [a] -> [a]
drop' n xs
    | n <= 0    = xs
drop' _ []      = []
drop' n (_:xs)  = drop' (n-1) xs

3.2 模式绑定与守卫 #

haskell
-- where中使用守卫
maxThree :: Int -> Int -> Int -> Int
maxThree x y z
    | x >= y && x >= z = x
    | y >= z           = y
    | otherwise        = z

-- 使用where简化
maxThree' :: Int -> Int -> Int -> Int
maxThree' x y z
    | x >= maxYZ = x
    | y >= z     = y
    | otherwise  = z
  where
    maxYZ = max y z

3.3 函数参数中的模式 #

haskell
-- 模式匹配参数后使用守卫
compareLength :: [a] -> [a] -> Ordering
compareLength xs ys
    | lenX < lenY = LT
    | lenX > lenY = GT
    | otherwise   = EQ
  where
    lenX = length xs
    lenY = length ys

四、复杂守卫 #

4.1 复合条件 #

haskell
-- 使用逻辑运算符
validateAge :: Int -> Bool
validateAge age
    | age < 0    = False
    | age > 150  = False
    | otherwise  = True

-- 使用&&和||
isValidEmail :: String -> Bool
isValidEmail email
    | '@' `notElem` email = False
    | '.' `notElem` afterAt = False
    | otherwise = True
  where
    afterAt = dropWhile (/= '@') email

-- 复杂条件
validatePassword :: String -> Bool
validatePassword pwd
    | length pwd < 8       = False
    | not (any isUpper pwd) = False
    | not (any isLower pwd) = False
    | not (any isDigit pwd) = False
    | otherwise            = True

4.2 嵌套守卫 #

haskell
-- 不推荐:嵌套守卫难以阅读
complexCheck :: Int -> Int -> String
complexCheck x y
    | x > 0 = 
        if y > 0 
            then "both positive"
            else "x positive"
    | y > 0 = "y positive"
    | otherwise = "both non-positive"

-- 推荐:使用模式匹配和where
complexCheck' :: Int -> Int -> String
complexCheck' x y
    | x > 0 && y > 0 = "both positive"
    | x > 0          = "x positive"
    | y > 0          = "y positive"
    | otherwise      = "both non-positive"

4.3 使用函数作为条件 #

haskell
-- 使用谓词函数
filter' :: (a -> Bool) -> [a] -> [a]
filter' _ [] = []
filter' p (x:xs)
    | p x       = x : filter' p xs
    | otherwise = filter' p xs

-- 使用
evens = filter' even [1..10]  -- [2,4,6,8,10]

五、守卫与where #

5.1 where中的定义 #

haskell
-- where定义在所有守卫中可见
calculate :: Double -> Double -> String
calculate a b
    | result > 100 = "Large: " ++ show result
    | result > 10  = "Medium: " ++ show result
    | otherwise    = "Small: " ++ show result
  where
    result = a * b

-- 多个where定义
analyze :: [Int] -> String
analyze xs
    | sumXs > 100 = "Sum large: " ++ show sumXs
    | lenXs > 10  = "List long: " ++ show lenXs
    | otherwise   = "Normal list"
  where
    sumXs = sum xs
    lenXs = length xs

5.2 where中的函数 #

haskell
-- where中定义辅助函数
quicksort :: Ord a => [a] -> [a]
quicksort [] = []
quicksort (x:xs) = smaller ++ [x] ++ larger
  where
    smaller = [a | a <- xs, a <= x]
    larger  = [b | b <- xs, b > x]

-- 复杂计算
solveQuadratic :: Double -> Double -> Double -> String
solveQuadratic a b c
    | discriminant < 0 = "No real roots"
    | discriminant == 0 = "One root: " ++ show root
    | otherwise = "Two roots: " ++ show root1 ++ ", " ++ show root2
  where
    discriminant = b * b - 4 * a * c
    root = -b / (2 * a)
    root1 = (-b + sqrt discriminant) / (2 * a)
    root2 = (-b - sqrt discriminant) / (2 * a)

六、守卫与let #

6.1 let在守卫中 #

haskell
-- let在守卫表达式中
process :: Int -> String
process x
    | let doubled = x * 2 in doubled > 100 = "Large: " ++ show doubled
    | otherwise = "Small"

6.2 let与where选择 #

haskell
-- 使用where(推荐)
calculateTax :: Double -> Double
calculateTax income
    | income <= threshold = income * 0.1
    | otherwise           = threshold * 0.1 + (income - threshold) * 0.2
  where
    threshold = 50000

-- 使用let
calculateTax' :: Double -> Double
calculateTax' income =
    let threshold = 50000
    in if income <= threshold
        then income * 0.1
        else threshold * 0.1 + (income - threshold) * 0.2

七、case与守卫 #

7.1 case中使用守卫 #

haskell
-- case表达式中的守卫
describeList :: [a] -> String
describeList xs = case xs of
    []  -> "empty list"
    [x] -> "singleton list"
    [x, y]
        | x == y -> "two equal elements"
        | otherwise -> "two different elements"
    _   -> "long list"

7.2 选择case还是守卫 #

haskell
-- 使用守卫:基于条件
classifyNumber :: Int -> String
classifyNumber n
    | n < 0     = "negative"
    | n == 0    = "zero"
    | otherwise = "positive"

-- 使用case:基于模式
describeMaybe :: Maybe Int -> String
describeMaybe m = case m of
    Nothing -> "nothing"
    Just 0  -> "zero"
    Just n  -> "number: " ++ show n

八、实践示例 #

8.1 验证函数 #

haskell
-- 验证用户输入
validateUser :: String -> Int -> Maybe String
validateUser name age
    | null name        = Just "Name cannot be empty"
    | length name > 50 = Just "Name too long"
    | age < 0          = Just "Invalid age"
    | age > 150        = Just "Age too high"
    | otherwise        = Nothing

8.2 数学函数 #

haskell
-- 分段函数
piecewise :: Double -> Double
piecewise x
    | x < 0     = -x
    | x < 1     = x * x
    | x < 2     = 2 * x - 1
    | otherwise = x * x - 2 * x + 2

-- 阶乘(带验证)
factorial :: Integer -> Integer
factorial n
    | n < 0     = error "negative argument"
    | n == 0    = 1
    | otherwise = n * factorial (n - 1)

8.3 列表处理 #

haskell
-- 安全取元素
safeIndex :: [a] -> Int -> Maybe a
safeIndex xs n
    | n < 0     = Nothing
    | n >= length xs = Nothing
    | otherwise = Just (xs !! n)

-- 分割列表
splitAt' :: Int -> [a] -> ([a], [a])
splitAt' n xs
    | n <= 0    = ([], xs)
    | otherwise = go n xs []
  where
    go 0 xs acc = (reverse acc, xs)
    go _ [] acc = (reverse acc, [])
    go n (x:xs) acc = go (n-1) xs (x:acc)

8.4 字符串处理 #

haskell
-- 清理字符串
cleanString :: String -> String
cleanString s
    | null trimmed = ""
    | otherwise    = trimmed
  where
    trimmed = strip s
    strip = dropWhile isSpace . reverse . dropWhile isSpace . reverse

-- 首字母大写
capitalize :: String -> String
capitalize [] = []
capitalize (x:xs)
    | isLetter x = toUpper x : xs
    | otherwise  = x:xs

九、最佳实践 #

9.1 可读性 #

haskell
-- 好:清晰的守卫
classify :: Int -> String
classify n
    | n < 0     = "negative"
    | n == 0    = "zero"
    | n > 0     = "positive"

-- 不好:复杂的单行条件
classifyBad n
    | n < 0 = "negative"
    | n == 0 = "zero"
    | True = "positive"  -- 使用True而不是otherwise

9.2 完整性 #

haskell
-- 好:使用otherwise确保完整性
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv x y
    | y == 0    = Nothing  -- 冗余但清晰
    | otherwise = Just (x `div` y)

-- 更好:模式匹配处理特殊情况
safeDiv' :: Int -> Int -> Maybe Int
safeDiv' _ 0 = Nothing
safeDiv' x y = Just (x `div` y)

9.3 避免重复 #

haskell
-- 不好:重复条件
badExample x y
    | x > 0 && y > 0 = "both positive"
    | x > 0 && y <= 0 = "x positive"
    | x <= 0 && y > 0 = "y positive"
    | x <= 0 && y <= 0 = "both non-positive"

-- 好:简化条件
goodExample x y
    | x > 0 && y > 0 = "both positive"
    | x > 0          = "x positive"
    | y > 0          = "y positive"
    | otherwise      = "both non-positive"

十、总结 #

守卫表达式要点:

  1. 基本语法:使用 | 分隔条件和结果
  2. 多守卫:按顺序检查多个条件
  3. otherwise:作为默认情况
  4. 与模式匹配结合:先模式匹配,后守卫
  5. where使用:where定义对所有守卫可见
  6. 复合条件:使用 &&|| 组合条件
  7. 最佳实践:保持可读性,确保完整性

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

最后更新:2026-03-27