Haskell中Data.Function.on有何作用?求示例及高效应用场景
Haskell中
on函数的用法示例与场景 on是Data.Function模块里的工具函数,核心作用是把一个二元函数“包装”后,让它先对两个输入执行同一个转换操作,再用原二元函数处理转换后的结果。它的类型签名和等价实现很直白:
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c on op f x y = op (f x) (f y)
基础使用示例
1. 按属性比较/排序
比如对字符串列表按长度排序,不用on的话需要写冗余的lambda,用on能直接把逻辑拆解开:
import Data.Function (on) import Data.List (sortBy) -- 按字符串长度从长到短排序 sortByLengthDesc :: [String] -> [String] sortByLengthDesc = sortBy (compare `on` length)
这里compare on length完全等价于\x y -> compare (length x) (length y),但代码更简洁直观。
再比如处理自定义类型,按用户的年龄字段比较:
data User = User { name :: String, age :: Int } deriving (Show) -- 判断两个用户年龄是否相同 sameAge :: User -> User -> Bool sameAge = (==) `on` age -- 按年龄升序排序用户列表 sortUsersByAge :: [User] -> [User] sortUsersByAge = sortBy (compare `on` age)
2. 数值运算的包装
比如计算两个数的绝对值之和,用on可以避免重复写abs:
absSum :: Int -> Int -> Int absSum = (+) `on` abs -- 等价于 \x y -> abs x + abs y
让代码更简洁的场景案例
1. 分组操作
把列表元素按某个属性分组时,on能大幅简化lambda:
import Data.List (groupBy) -- 按字符串长度分组 groupByLength :: [String] -> [[String]] groupByLength = groupBy ((==) `on` length)
不用on的话需要写groupBy (\x y -> length x == length y),重复的length调用显得冗余。
2. 元组的属性排序
对元组列表按第二个元素排序,on配合snd能让代码更简洁:
-- 按元组的第二个元素升序排序 sortBySecond :: Ord b => [(a, b)] -> [(a, b)] sortBySecond = sortBy (compare `on` snd)
如果不用on,得写sortBy (\(x1,y1) (x2,y2) -> compare y1 y2),不仅代码更长,还容易写错元组索引。
3. 复杂转换的复用
当转换逻辑比较复杂时,on能让你只定义一次转换函数,再复用在不同的二元操作上:
-- 提取字符串的第一个字符(处理空字符串的情况) firstChar :: String -> Char firstChar [] = '_' firstChar (c:_) = c -- 判断两个字符串首字符是否相同 sameFirstChar :: String -> String -> Bool sameFirstChar = (==) `on` firstChar -- 按首字符的字典序排序 sortByFirstChar :: [String] -> [String] sortByFirstChar = sortBy (compare `on` firstChar)
为什么on实用?
- 减少重复代码:避免在lambda里重复写相同的转换逻辑,尤其是转换函数复杂时,只需要定义一次。
- 提高可读性:代码意图更清晰——先明确要做什么转换,再明确要执行什么二元操作,结构一目了然。
- 贴合函数式风格:用函数组合代替冗余lambda,更符合Haskell的函数式编程思想。
内容的提问来源于stack exchange,提问作者qwertyuiop
相关产品推荐
相关产品推荐

