函数组合 #

一、组合基础 #

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

十、总结 #

函数组合要点:

  1. 组合运算符(.) 连接两个函数
  2. 执行顺序:从右向左执行
  3. $运算符:低优先级函数应用
  4. 无点风格:省略参数的简洁写法
  5. 组合链:多个函数串联
  6. 组合律:满足结合律和恒等律
  7. 最佳实践:平衡简洁与可读性

掌握函数组合后,让我们继续学习列表与元组。

最后更新:2026-03-27