守卫表达式 #
一、守卫基础 #
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"
十、总结 #
守卫表达式要点:
- 基本语法:使用
|分隔条件和结果 - 多守卫:按顺序检查多个条件
- otherwise:作为默认情况
- 与模式匹配结合:先模式匹配,后守卫
- where使用:where定义对所有守卫可见
- 复合条件:使用
&&、||组合条件 - 最佳实践:保持可读性,确保完整性
掌握守卫表达式后,让我们继续学习Lambda表达式。
最后更新:2026-03-27