函数组合 #
一、组合基础 #
1.1 组合运算符 #
函数组合使用 . 运算符:
haskell
-- 组合运算符定义
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)
1.2 基本用法 #
haskell
-- 简单组合
doubleAndAddOne :: Int -> Int
doubleAndAddOne = (+1) . (*2)
-- 等价于
doubleAndAddOne' x = (x * 2) + 1
-- 使用
doubleAndAddOne 5 -- 11
1.3 组合顺序 #
haskell
-- 组合从右向左执行
f . g . h $ x
-- 等价于
f (g (h x))
-- 示例
process = show . (*2) . (+1)
process 5 -- "12"
-- 执行顺序:(+1) -> (*2) -> show
-- 5 + 1 = 6
-- 6 * 2 = 12
-- show 12 = "12"
二、组合与函数应用 #
2.1 $运算符 #
haskell
-- $运算符定义
($) :: (a -> b) -> a -> b
f $ x = f x
-- 优先级:$最低,.次之
-- 使用$避免括号
result = sum (map (*2) [1..10])
result' = sum $ map (*2) [1..10]
2.2 组合与$配合 #
haskell
-- 组合链使用$
pipeline = show . sum . filter even
result = pipeline $ [1..10] -- "30"
-- 等价于
result' = (show . sum . filter even) [1..10]
2.3 多层嵌套简化 #
haskell
-- 复杂嵌套
nested = f (g (h (i x)))
-- 使用组合
nested' = f . g . h . i $ x
-- 使用$
nested'' = f $ g $ h $ i x
三、无点风格 #
3.1 什么是无点风格 #
无点风格(Point-free style)是不显式使用参数的写法:
haskell
-- 有参数形式
sumOfSquares xs = sum (map (^2) xs)
-- 无点形式
sumOfSquares = sum . map (^2)
3.2 转换规则 #
haskell
-- 规则:f x = g (h x) => f = g . h
-- 示例1
-- 有参数
doubleAll xs = map (*2) xs
-- 无点
doubleAll = map (*2)
-- 示例2
-- 有参数
evenLength xs = even (length xs)
-- 无点
evenLength = even . length
-- 示例3
-- 有参数
firstChars xs = map head xs
-- 无点
firstChars = map head
3.3 复杂无点转换 #
haskell
-- 复杂示例
-- 有参数
process xs = sum (filter even (map (*2) xs))
-- 无点
process = sum . filter even . map (*2)
-- 带条件
-- 有参数
classify x = if x > 0 then "positive" else "non-positive"
-- 无点(需要辅助)
classify = if' (>0) "positive" "non-positive"
where if' p t f x = if p x then t else f
四、组合链 #
4.1 构建组合链 #
haskell
-- 多函数组合
pipeline :: [Int] -> String
pipeline = show . sum . filter even . map (*2)
-- 执行过程
-- [1..10]
-- -> map (*2) -> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
-- -> filter even -> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
-- -> sum -> 110
-- -> show -> "110"
4.2 可读性 #
haskell
-- 好:清晰的组合链
processData :: [Int] -> Int
processData = sum . filter positive . map double
where
positive = (>0)
double = (*2)
-- 不好:过长的组合链
badPipeline = f . g . h . i . j . k . l . m
4.3 分组组合 #
haskell
-- 分组提高可读性
complexProcess :: [Int] -> String
complexProcess = format . aggregate . transform
where
transform = map (*2) . filter even
aggregate = sum . take 10
format = show . ("Result: " ++)
五、组合技巧 #
5.1 flip与组合 #
haskell
-- flip改变参数顺序
flip :: (a -> b -> c) -> b -> a -> c
flip f y x = f x y
-- 使用flip
subtractFrom = flip (-)
subtractFrom 10 5 -- -5 (实际是 5 - 10)
-- 组合中使用flip
takeFromEnd n = reverse . take n . reverse
5.2 const与组合 #
haskell
-- const返回第一个参数
const :: a -> b -> a
const x _ = x
-- 使用const
alwaysTrue = const True
alwaysTrue "anything" -- True
-- 组合中使用
ignoreInput = const . show
ignoreInput 5 "ignored" -- "5"
5.3 id与组合 #
haskell
-- id是恒等函数
id :: a -> a
id x = x
-- 作为组合的零元素
f . id = f
id . f = f
-- 在fold中使用
composeAll = foldr (.) id [f, g, h]
-- 等价于 f . g . h
六、组合与高阶函数 #
6.1 map与组合 #
haskell
-- map中的组合
map (show . (*2)) [1, 2, 3] -- ["2", "4", "6"]
-- 等价于
map show (map (*2) [1, 2, 3])
-- 组合律
map (f . g) = map f . map g
6.2 filter与组合 #
haskell
-- filter条件组合
filter (even . (*3)) [1..10] -- [2, 4, 6, 8, 10]
-- 等价于
filter (\x -> even (x * 3)) [1..10]
-- 多条件组合
filter ((>5) . (*2)) [1..10] -- [3, 4, 5, 6, 7, 8, 9, 10]
6.3 fold与组合 #
haskell
-- fold组合函数
composeList :: [a -> a] -> (a -> a)
composeList = foldr (.) id
-- 使用
applyAll = composeList [(*2), (+1), negate]
applyAll 5 -- -12
-- 执行:negate ((5 + 1) * 2) = -12
七、实践示例 #
7.1 数据转换管道 #
haskell
-- 处理用户数据
type User = (String, Int) -- (name, age)
-- 获取成年用户名字
adultNames :: [User] -> [String]
adultNames = map fst . filter isAdult
where
isAdult (_, age) = age >= 18
-- 按年龄排序后取名字
sortedNames :: [User] -> [String]
sortedNames = map fst . sortBy compareAge
where
compareAge (_, a1) (_, a2) = compare a1 a2
7.2 字符串处理 #
haskell
import Data.Char
-- 清理字符串
clean :: String -> String
clean = filter (not . isSpace) . map toLower
-- 统计字符
countChar :: Char -> String -> Int
countChar c = length . filter (== c)
-- 单词处理
processWords :: String -> [String]
processWords = map (filter isLetter) . words
7.3 数学计算 #
haskell
-- 链式计算
calculate :: Double -> Double
calculate = sqrt . abs . negate . (*2)
-- 列表处理
processNumbers :: [Int] -> Int
processNumbers = sum . filter even . map abs . take 10
-- 组合数计算
factorial :: Integer -> Integer
factorial = product . enumFromTo 1
7.4 验证函数 #
haskell
-- 组合验证器
validateAll :: [a -> Bool] -> a -> Bool
validateAll = and . sequence
-- 示例验证器
isLongEnough = (>5) . length
hasUpperCase = any isUpper
hasLowerCase = any isLower
-- 组合验证
validPassword :: String -> Bool
validPassword = validateAll [isLongEnough, hasUpperCase, hasLowerCase]
八、组合律 #
8.1 结合律 #
haskell
-- 组合满足结合律
(f . g) . h = f . (g . h)
-- 因此可以省略括号
f . g . h . i
8.2 恒等律 #
haskell
-- id是组合的单位元
f . id = f
id . f = f
8.3 分配律 #
haskell
-- map与组合
map (f . g) = map f . map g
-- filter与逻辑运算
filter (p && q) = filter p . filter q
filter (p || q) xs = filter p xs ++ filter q xs
九、最佳实践 #
9.1 何时使用组合 #
haskell
-- 好:简单的数据转换管道
process = sum . filter even . map (*2)
-- 好:无点风格提高可读性
lengthEven = even . length
-- 不好:过度复杂的组合
tooComplex = f . g . h . i . j . k . l . m . n
9.2 可读性平衡 #
haskell
-- 有时显式参数更清晰
-- 好
sumOfSquares xs = sum (map (^2) xs)
-- 也清晰
sumOfSquares' = sum . map (^2)
-- 复杂情况使用where
complexCalc xs = result
where
result = sum filtered
filtered = filter even doubled
doubled = map (*2) xs
9.3 调试技巧 #
haskell
-- 使用trace调试组合链
import Debug.Trace
debugPipeline = show . trace "after sum" . sum
. trace "after filter" . filter even
. trace "after map" . map (*2)
-- 分步调试
step1 = map (*2) [1..5]
step2 = filter even step1
step3 = sum step2
十、总结 #
函数组合要点:
- 组合运算符:
(.)连接两个函数 - 执行顺序:从右向左执行
- $运算符:低优先级函数应用
- 无点风格:省略参数的简洁写法
- 组合链:多个函数串联
- 组合律:满足结合律和恒等律
- 最佳实践:平衡简洁与可读性
掌握函数组合后,让我们继续学习列表与元组。
最后更新:2026-03-27