文章目录

Haskell 高阶函数:map、filter、foldr

发布于 2026-04-18 00:18:26 · 浏览 7 次 · 评论 0 条

Haskell 高阶函数:map、filter、foldr

在 Haskell 中,高阶函数是指接收其他函数作为参数或返回函数的函数。它们是函数式编程的核心工具,能帮你用极简的代码处理列表数据。

以下介绍 mapfilterfoldr 三个最常用的函数。


一、使用 map 转换列表元素

map 函数用于将一个函数应用到列表的每一个元素上,并生成一个新的列表。原列表保持不变。

1. 定义转换逻辑
确定你想要对每个元素执行的操作。例如,将列表中的每个数字乘以 2,或者将每个单词转换为大写。

2. 编写基础代码
假设我们要计算一个数字列表中每个数的平方。打开 GHCi 或代码编辑器,输入以下代码:

square :: Int -> Int
square x = x * x

numbers :: [Int]
numbers = [1, 2, 3, 4, 5]

squaredNumbers :: [Int]
squaredNumbers = map square numbers

3. 使用匿名函数简化
如果不想单独定义 square 函数,可以直接在 map 中使用匿名函数(Lambda 表达式)。执行如下操作:

squaredNumbers :: [Int]
squaredNumbers = map (\x -> x * x) [1, 2, 3, 4, 5]

4. 利用柯里化简化
对于常见操作(如加法、乘法),可以直接传入部分应用的运算符。运行以下代码查看结果:

-- 将每个元素加 1
result :: [Int]
result = map (+1) [1, 2, 3, 4, 5]
-- 结果为: [2, 3, 4, 5, 6]

二、使用 filter 筛选列表元素

filter 函数根据给定的条件(返回布尔值的谓词函数)保留列表中满足该条件的元素。

1. 设定筛选条件
明确你需要保留哪些元素。例如,只保留偶数,或者保留长度大于 3 的字符串。

2. 定义谓词函数
编写一个判断函数。例如,判断一个数是否为偶数:

isEven :: Int -> Bool
isEven x = x `mod` 2 == 0

3. 应用 filter 函数
将谓词函数和列表传入 filter执行以下代码:

evenNumbers :: [Int]
evenNumbers = filter isEven [1, 2, 3, 4, 5, 6]
-- 结果为: [2, 4, 6]

4. 组合使用高阶函数
filter 常与 map 配合使用。例如,先筛选出偶数,再计算它们的平方:

process :: [Int]
process = map (^2) (filter isEven [1..10])
-- 逻辑解释: 先从 1 到 10 中筛选出偶数,然后对这些偶数进行平方运算
-- 结果为: [4, 16, 36, 64, 100]

三、使用 foldr 折叠列表

foldr(fold right)是一个递归的高阶函数,它将一个二元函数从一个初始值开始,从列表的右侧(末尾)开始向左结合,将列表折叠(归约)为一个单一的值。

1. 理解右折叠逻辑
foldr 接受三个参数:一个二元函数、一个初始值和一个列表。其运算逻辑遵循以下数学表达:

对于列表 [x1, x2, ..., xn] 和初始值 z
$$ f \text{ } x1 \text{ } (f \text{ } x2 \text{ } (... (f \text{ } xn \text{ } z))) $$

这意味着运算从列表的最右边开始,逐步向左结合。

2. 求和操作示例
虽然求和通常使用 foldl,但 foldr 也可以做到。输入以下代码:

sumList :: [Int]
sumList = foldr (+) 0 [1, 2, 3]
-- 计算过程: 1 + (2 + (3 + 0)) = 6

3. 减法操作示例(区分左右折叠的关键)
使用非交换运算(如减法)最能体现 foldr 的特性。观察以下代码的计算过程:

diffResult :: Int
diffResult = foldr (-) 0 [1, 2, 3]

为了直观理解这个折叠过程,可以参考下面的逻辑流向图:

graph LR A["Head: 1"] -- "-" --> B["Head: 2"] B -- "-" --> C["Head: 3"] C -- "-" --> D["Init: 0"]

上图展示了递归调用的结构。代码实际执行的运算顺序为:
$$ 1 - (2 - (3 - 0)) $$
计算步骤如下:

  1. 先算最内层:$3 - 0 = 3$
  2. 向左结合:$2 - 3 = -1$
  3. 最后结合:$1 - (-1) = 2$

最终结果为 2

4. 构建数据结构(进阶用法)
foldr 非常适合用于从右向左构建数据结构。例如,使用 foldr 实现一个 map 功能:

mapUsingFoldr :: (a -> b) -> [a] -> [b]
mapUsingFoldr f xs = foldr (\x acc -> f x : acc) [] xs

5. 处理无限列表
由于 foldr 的惰性求值特性,它可以在某些条件下处理无限列表(只要组合函数的非严格性允许),而 foldl 无法做到。


通过熟练运用 map 进行转换、filter 进行筛选以及 foldr 进行归约,你可以用声明式的方式编写出简洁且强大的 Haskell 代码。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文