reduce()、accumulate() 与 walk()
折叠列表、构建累计结果,并使用 walk() 应用副作用
reduce()、accumulate() 与 walk() 是 CoddyKit 上的免费 R Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 R Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 R Academy 课程共包含 4 节课。
超越 map() — 其他 purrr 动词
map() 会独立地对每个元素应用函数,而有些任务需要组合元素(reduce)、累积中间结果,或仅为产生副作用而应用函数(walk)。purrr 为每种情况都提供了专用函数。
library(purrr)
# Example: combining a list of numbers into a single value
nums <- list(1, 2, 3, 4, 5)
# We want: 1 + 2 + 3 + 4 + 5 = 15
# map() can't do this — it returns 5 separate results
# reduce() folds the list into one value
result <- reduce(nums, `+`)
cat('Sum via reduce:', result)reduce() — 将列表折叠为一个值
reduce(.x, .f, .init) 会从左到右,对 .x 中的元素累计应用 .f,将列表归约为单个值。reduce(list(a,b,c), f) 会计算 f(f(a,b),c)。
library(purrr)
# reduce with max: find the overall maximum
values <- list(42, 17, 89, 55, 23)
result <- reduce(values, max)
cat('Maximum:', result, '\n')
# reduce with paste: concatenate strings
words <- list('The', 'quick', 'brown', 'fox')
reduce(words, paste)使用 .init 调用 reduce()
.init 参数提供一个起始(seed)值。第一次调用会变为 f(.init, x[1])。当列表可能为空时(此时结果为 .init),或者您需要指定起始状态时,该参数非常重要。
library(purrr)
# Running count of elements matching a condition
data <- list(5, 12, 3, 18, 7, 15, 2)
# Count values greater than 10, starting from 0
reduce(data, function(count, x) count + (x > 10), .init = 0L)
# Also useful for string building:
reduce(c('A','B','C'), paste, sep='-', .init='START')使用数据框调用 reduce() — 链式连接
reduce() 最强大的用途之一是连接数据框列表。您无需编写 left_join(left_join(df1, df2), df3),而可以使用 reduce(list_of_dfs, left_join, by='key')。
library(purrr)
library(dplyr)
base_df <- data.frame(id=1:3, name=c('Alice','Bob','Carol'))
scores <- data.frame(id=1:3, score=c(85,90,78))
grades <- data.frame(id=1:3, grade=c('B','A','C'))
city <- data.frame(id=1:3, city=c('NYC','LA','Chicago'))
dfs <- list(base_df, scores, grades, city)
reduce(dfs, left_join, by='id')accumulate() — 保留中间结果
accumulate(.x, .f) 类似于 reduce(),但会保留所有中间值。它返回一个与输入长度相同的向量(或列表),展示每一步之后的累计结果。
library(purrr)
# Running sum — same as cumsum() but via accumulate
values <- c(100, 120, 95, 140, 160)
accumulate(values, `+`)
# Running maximum
accumulate(c(50, 52, 48, 55, 53, 58), max)使用 accumulate() 获取逐步结果
accumulate() 特别适合追踪算法步骤、逐字符构建字符串,或处理任何需要检查每个中间状态而不仅是最终结果的过程。
library(purrr)
# Compound interest step by step
principal <- 1000
rates <- c(0.05, 0.05, 0.05, 0.05, 0.05) # 5% per year
accumulate(rates, function(balance, rate) {
round(balance * (1 + rate), 2)
}, .init = principal)walk() — 只产生副作用,不返回结果
walk(.x, .f) 会对 .x 的每个元素应用 .f,目的仅是产生副作用(打印、写入文件、发送消息)。它会隐式返回 .x,因此可以在处理流程中使用,而不会中断链式调用。
library(purrr)
# Print a summary for each dataset
datasets <- list(
mtcars = mtcars[,1:3],
iris = iris[,1:3]
)
walk(datasets, function(df) {
cat('Rows:', nrow(df), '| Cols:', ncol(df),
'| NAs:', sum(is.na(df)), '\n')
})在处理流程中使用 walk()
由于 walk() 会隐式返回 .x,您可以将它插入 dplyr 处理流程中,以记录或打印中间结果,而不会中断流程。可以把它理解为用于调试或记录的观察点。
library(purrr)
library(dplyr)
results <- list(
East = data.frame(rep=c('A','B'), sales=c(100,120)),
West = data.frame(rep=c('C','D'), sales=c(200,190))
)
# Log each region's data, then continue processing
region_totals <- results %>%
walk(~cat('Processing:', nrow(.x), 'rows\n')) %>%
map_dbl(~sum(.x$sales))
print(region_totals)walk2() — 对两个输入产生副作用
walk2(.x, .y, .f) 是 walk() 的双输入版本。一个经典用例是将多个数据框写入文件:其中 .x 是数据框列表,.y 是文件路径列表。
library(purrr)
data_list <- list(
east = data.frame(x=1:3, y=4:6),
west = data.frame(x=7:9, y=10:12)
)
file_paths <- list(
'/tmp/east_data.csv',
'/tmp/west_data.csv'
)
# Write each data frame to its corresponding path
walk2(data_list, file_paths, function(df, path) {
write.csv(df, path, row.names=FALSE)
cat('Written:', path, '\n')
})iwalk() — 带索引遍历
iwalk(.x, .f) 类似于 walk(),但会将元素名称(或索引)作为第二个参数 .y 传入。当您需要知道副作用操作正在处理哪个元素时,这非常方便。
library(purrr)
region_counts <- list(East=150, West=200, North=85, South=120)
# Print each region with its rank
iwalk(region_counts, function(count, name) {
cat(name, ':', count, 'customers\n')
})reduce_right() — 从右到左归约
reduce(.x, .f, .dir='backward')(或已弃用的 reduce_right())会从右到左应用函数:f(a, f(b, f(c, d)))。对于减法或构建字符串等不可交换运算,这一点很重要。
library(purrr)
words <- c('fox','brown','quick','The')
# Left-to-right: 'The quick brown fox'
reduce(rev(words), paste)
# Right-to-left: same effect but starting from right
reduce(words, function(a, b) paste(b, a))快速检查
什么时候应该使用 walk() 而不是 map()?
回顾:reduce、accumulate、walk
关于 reduce、accumulate 和 walk 的要点:
reduce(.x, .f)— 将列表从左到右折叠为一个值;.init设置起始值reduce(list_of_dfs, left_join)— 用于链式连接的强大模式accumulate(.x, .f)— 类似 reduce,但返回所有中间结果walk(.x, .f)— 为产生副作用而应用函数(打印、写入、记录);返回.xwalk2(.x, .y, .f)— 双输入 walk(例如数据框 + 文件路径)iwalk(.x, .f)— 将元素名称作为第二个参数的 walk
library(purrr)
library(dplyr)
# reduce() joins a list of data frames, accumulate() shows interim states
monthly_sums <- c(100, 120, 95, 140, 160, 130)
cat('reduce (total): ', reduce(monthly_sums, `+`), '\n')
cat('accumulate (running):', accumulate(monthly_sums, `+`), '\n')
# walk() for side-effect logging
results <- list(a=42, b=17, c=89)
walk(results, ~cat('Value:', .x, '\n'))常见问题解答
「reduce()、accumulate() 与 walk()」课时是免费的吗?
是的 — 「reduce()、accumulate() 与 walk()」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 R Academy 课程的其余内容,请升级到 CoddyKit PRO。 R Academy 课程共包含 4 节课。
「reduce()、accumulate() 与 walk()」这节课中我会学到什么?
折叠列表、构建累计结果,并使用 walk() 应用副作用 你通过在浏览器中直接运行的动手代码来练习 R Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 R Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 R Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「reduce()、accumulate() 与 walk()」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 R Academy 课中编写并运行代码吗?
能。每节 R Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- map() 与类型化变体
- 使用 map2() 和 pmap() 处理多个输入
- reduce()、accumulate() 与 walk()
- keep()、discard() 与列表筛选