← 返回题目列表

Python 的 map、filter、reduce 怎么用?和列表推导式比哪个好?

中等 第 17 / 21 题 更新于 2026/07/31
Pythonmapfilterreduce

简化版

**mapfilterreduce 是函数式编程的三个基本工具:map(f, it) 把函数 f 作用到每个元素(映射/转换)、filter(f, it) 保留 f 返回真的元素(过滤/筛选)、reduce(f, it) 用二元函数把序列「折叠」成一个值(归约/累积)。**要点:① map/filter 返回惰性迭代器(Python 3,不是列表,只能遍历一次、省内存),要列表得 list(map(...));② reducefunctools(Python 3 从内置移到 functools,因为 Guido 认为它可读性差),reduce(lambda a,b: a+b, [1,2,3,4]) = ((1+2)+3)+4 = 10,可带初始值 reduce(f, it, init);③ 和列表推导式比——map/filter 大多数场景不如推导式直观([f(x) for x in it][x for x in it if cond]),Python 社区更推荐推导式;但 map(内置函数, it)(如 map(str, nums))时 map 更简洁、且惰性省内存。reduce 的替代:求和用 sum、最大用 max、连接用 "".join、累积用 itertools.accumulate——这些内置比 reduce 更清晰。核心记忆:map 映射/filter 过滤/reduce 归约;map/filter 返回惰性迭代器,多数场景用列表推导式更 Pythonic;reduce 在 functools 里、能用 sum/max/join 就别用 reduce。

详细版

三个函数对比

函数作用返回推荐替代
map(f, it)每个元素应用 f惰性迭代器[f(x) for x in it]
filter(f, it)保留 f 为真的惰性迭代器[x for x in it if f(x)]
reduce(f, it)折叠成一个值单个值sum/max/join/accumulate
from functools import reduce

nums = [1, 2, 3, 4, 5]

# map:映射(每个元素转换)
list(map(lambda x: x**2, nums))     # [1, 4, 9, 16, 25]
list(map(str, nums))                # ['1','2','3','4','5'](内置函数时简洁)
# map 多个可迭代:并行取
list(map(lambda a,b: a+b, [1,2], [10,20]))   # [11, 22]

# filter:过滤(保留真的)
list(filter(lambda x: x % 2 == 0, nums))     # [2, 4]
list(filter(None, [0, 1, "", "a", None]))    # [1, 'a'](None=保留真值)

# reduce:归约(折叠成一个值)
reduce(lambda a, b: a + b, nums)             # 15(((((1+2)+3)+4)+5))
reduce(lambda a, b: a * b, nums)             # 120(连乘)
reduce(lambda a, b: a + b, nums, 100)        # 115(带初始值 100)
reduce(max, nums)                            # 5(找最大)

# 惰性:map/filter 是迭代器(只遍历一次)
m = map(str, nums)
list(m)     # ['1',...]
list(m)     # [](耗尽了!)

# vs 列表推导式(多数场景更推荐)
[x**2 for x in nums]                 # 比 map(lambda) 直观
[x for x in nums if x % 2 == 0]      # 比 filter(lambda) 直观

# reduce 的更好替代
sum(nums)                            # 15(别用 reduce 求和)
max(nums)                            # 5
"".join(["a", "b", "c"])            # 'abc'(别用 reduce 拼字符串)

⚠️ 实用结论:map/filter/reduce 是函数式思想的经典三件套,但在 Python 里它们大多数场景「不如列表推导式和内置函数清晰」——Python 是「多范式但偏命令式/推导式」的语言,社区共识是优先用推导式。具体建议:① 转换用推导式 [f(x) for x in it]map(lambda x: f(x), it) 直观,但当 f 是「现成的函数/内置函数」时 map(str, nums)map(int, strs) 反而更简洁(不用写 lambda);② 筛选用推导式 [x for x in it if cond]filter(lambda...) 清晰;③ reduce 尽量避免——Guido 当年就想删掉它(可读性差、要在脑子里模拟折叠过程),能用 sum(求和)、max/min(极值)、"".join(拼接)、math.prod(连乘,3.8+)、itertools.accumulate(累积中间结果)这些「专门的、有名字的」内置就别用 reduce。真正适合 reduce 的是「没有现成内置的、自定义的折叠逻辑」(如按自定义规则合并字典)。关键记住 map/filter 返回惰性迭代器(省内存、只能遍历一次),这既是优点(大数据)也是坑(想复用要先 list())。

完整版教学

一、map:映射

先理解 map:

map(func, iterable, ...):把 func 作用到每个元素
  → 返回一个"每个元素都被 func 转换后"的迭代器

单个可迭代:
  map(f, [a, b, c])  → 迭代产出 f(a), f(b), f(c)
  list(map(lambda x: x*2, [1,2,3]))  → [2, 4, 6]

多个可迭代(并行取):
  map(f, [1,2,3], [10,20,30])
  → f(1,10), f(2,20), f(3,30)
  → 以最短的为准(多余的丢弃,同 zip)

返回惰性迭代器(Python 3):
  map(...) 不立即计算,边遍历边算
  → 省内存(大数据友好)、只能遍历一次

什么时候 map 比推导式好:
  ✓ func 是现成函数/内置(不用写 lambda):
    map(str, nums)         # 比 [str(x) for x in nums] 略简洁
    map(int, str_list)
    map(len, list_of_lists)
  ✗ func 要写 lambda:
    map(lambda x: x*2, nums)  # 不如 [x*2 for x in nums]

配合 * 解包:
  # 转置或批量应用
  list(map(list, zip(*matrix)))

所以 map(f,it)每个元素应用 f 返回惰性迭代器;func 是现成函数时比推导式简洁

map(func, iterable, ...) 把 func 作用到每个元素——返回一个每个元素都被 func 转换后的迭代器。单个可迭代map(f, [a,b,c]) 产出 f(a), f(b), f(c)多个可迭代(并行取)map(f, [1,2,3], [10,20,30]) 产出 f(1,10), f(2,20), f(3,30)(以最短为准、同 zip)。返回惰性迭代器(Python 3):不立即计算、边遍历边算(省内存、只能遍历一次)。什么时候 map 比推导式好:func 是现成函数/内置时(map(str, nums)[str(x) for x in nums] 略简洁),func 要写 lambda 时不如推导式(map(lambda x: x*2, nums) 不如 [x*2 for x in nums])。理解「map(f,it)每个元素应用 f 返回惰性迭代器;多可迭代并行取(以最短为准);func 是现成函数(str/int/len)时比推导式简洁、要写 lambda 不如推导式」,就掌握了 map。

二、filter:过滤

理解 filter:

filter(func, iterable):保留 func 返回真的元素
  → 返回"只含满足条件元素"的迭代器

  filter(lambda x: x > 0, [-1, 2, -3, 4])  → 2, 4
  → func(x) 为真则保留,为假则丢弃

func 为 None 的特殊用法:
  filter(None, iterable) → 保留所有"真值"元素
  filter(None, [0, 1, "", "a", None, [], [5]])
  → [1, 'a', [5]](去掉所有假值:0, "", None, [])
  → 相当于"去除假值"的快捷方式

返回惰性迭代器(同 map):
  filter(...) 惰性、只遍历一次

vs 列表推导式:
  filter(lambda x: cond(x), it)  ≈  [x for x in it if cond(x)]
  → 推导式通常更直观(尤其条件复杂时)
  filter(None, it)  ≈  [x for x in it if x]

filter + map 组合(先筛后转):
  # 偶数的平方
  list(map(lambda x: x**2, filter(lambda x: x%2==0, nums)))
  # 推导式更清晰:
  [x**2 for x in nums if x % 2 == 0]

itertools 里的相关:
  itertools.filterfalse(f, it)  # 保留 f 为假的(filter 的反面)
  itertools.compress(data, selectors)  # 按布尔选择器过滤

所以 filter(f,it)保留 f 为真的元素返回惰性迭代器;filter(None,it)去假值;推导式更直观

filter(func, iterable) 保留 func 返回真的元素——返回只含满足条件元素的迭代器(filter(lambda x: x > 0, [-1,2,-3,4]) → 2, 4)。func 为 None 的特殊用法filter(None, iterable) 保留所有真值元素(filter(None, [0,1,"","a",None,[],[5]])[1,'a',[5]],去掉所有假值,相当于「去除假值」的快捷方式)。返回惰性迭代器(同 map)vs 列表推导式filter(lambda x: cond(x), it)[x for x in it if cond(x)](推导式通常更直观、尤其条件复杂时)。filter + map 组合(先筛后转):推导式 [x**2 for x in nums if x % 2 == 0] 比嵌套的 map/filter 清晰。itertools 里的相关filterfalse(保留 f 为假的)、compress(按布尔选择器过滤)。理解「filter(f,it)保留 f 为真的返回惰性迭代器;filter(None,it)去假值(快捷);推导式更直观;filter+map 组合不如推导式;itertools.filterfalse/compress」,就掌握了 filter。

三、reduce:归约

理解 reduce 的折叠过程:

reduce(func, iterable[, initializer]):折叠成一个值
  func 是二元函数(接收 累积值, 当前元素)
  从左往右"累积",最终得到一个值

折叠过程(无初始值):
  reduce(f, [a, b, c, d])
  = f(f(f(a, b), c), d)
  → 第一步用前两个元素,之后每步把"累积结果"和下一个元素喂给 f

  reduce(lambda acc, x: acc + x, [1,2,3,4])
  step1: f(1, 2) = 3
  step2: f(3, 3) = 6
  step3: f(6, 4) = 10
  → 10

带初始值:
  reduce(f, [a,b,c], init)
  = f(f(f(init, a), b), c)
  → 从 init 开始折叠
  reduce(lambda acc,x: acc+x, [1,2,3], 100)  → 106
  好处:① 空序列时返回 init(无初始值时空序列报错)
        ② 累积值和元素类型可不同(如累积到字典/集合)

在 functools 里(不是内置):
  from functools import reduce
  → Python 3 把 reduce 从内置移到 functools
     (Guido 认为可读性差,不该鼓励)

reduce 的问题:
  ① 可读性差——要在脑子里模拟折叠过程
  ② 大多有更好的替代(见后)

真正适合 reduce 的场景:
  没有现成内置、自定义的折叠逻辑:
  reduce(lambda acc, d: {**acc, **d}, list_of_dicts, {})  # 合并字典
  reduce(operator.or_, list_of_sets)  # 并集

所以 reduce(f,it,init)用二元函数从左折叠成一个值;在 functools 里;可读性差

reduce(func, iterable[, initializer]) 折叠成一个值——func 是二元函数(接收累积值、当前元素)、从左往右累积。折叠过程(无初始值)reduce(f, [a,b,c,d]) = f(f(f(a,b),c),d)(第一步用前两个、之后每步把累积结果和下一个喂给 f);reduce(lambda acc,x: acc+x, [1,2,3,4])f(1,2)=3f(3,3)=6f(6,4)=10带初始值reduce(f, [a,b,c], init) = f(f(f(init,a),b),c)(从 init 开始,好处是空序列返回 init、累积值和元素类型可不同)。在 functools 里(不是内置):Python 3 把 reduce 移到 functools(Guido 认为可读性差)。reduce 的问题:可读性差(要脑内模拟折叠)、大多有更好替代。真正适合 reduce 的场景:没有现成内置的自定义折叠(合并字典 reduce(lambda acc,d: {**acc,**d}, ...)、并集 reduce(operator.or_, sets))。理解「reduce(f,it,init)二元函数从左折叠成一个值(f(f(a,b),c));带 init 从 init 开始(空序列返 init、类型可不同);在 functools 里;可读性差、有更好替代;适合自定义折叠(合并字典/并集)」,就掌握了 reduce。

四、和列表推导式的对比

理解为什么推导式更 Pythonic:

map/filter vs 列表推导式:

转换:
  map(lambda x: x*2, it)   →   [x*2 for x in it]
  map(str, it)             →   [str(x) for x in it] 或 map 略简洁

筛选:
  filter(lambda x: x>0, it)  →  [x for x in it if x>0]

筛选+转换:
  map(f, filter(g, it))    →   [f(x) for x in it if g(x)]
  → 推导式一行搞定、可读性远好于嵌套 map/filter

为什么推导式更 Pythonic:
  ① 直观——读起来像"对每个 x,如果...,取..."
  ② 一体——筛选+转换在一个表达式里(map+filter 要嵌套)
  ③ 不用写 lambda(map/filter 常要 lambda)
  ④ 可生成 list/set/dict/生成器(推导式统一语法)

map/filter 仍有优势的场景:
  ① 现成函数(不用 lambda):map(str, nums)、map(int, strs)
  ② 惰性省内存:map/filter 是迭代器(推导式的 [] 是即时列表)
     → 大数据用 (x for x in it)(生成器表达式)也惰性
  ③ 函数式风格代码库

惰性对比:
  map(f, it)          # 惰性迭代器
  [f(x) for x in it]  # 即时列表(占内存)
  (f(x) for x in it)  # 生成器表达式(惰性,等价 map)

选择:
  一般转换/筛选 → 列表推导式(或生成器表达式)
  现成函数映射 → map(略简洁)
  大数据惰性 → 生成器表达式 或 map/filter

所以推导式更 Pythonic(直观/一体/不写 lambda);map 现成函数时简洁、惰性省内存

map/filter vs 列表推导式:转换 map(lambda x: x*2, it)[x*2 for x in it]、筛选 filter(lambda x: x>0, it)[x for x in it if x>0]、筛选+转换 map(f, filter(g, it))[f(x) for x in it if g(x)](推导式一行、可读性远好于嵌套)。为什么推导式更 Pythonic① 直观、② 一体(筛选+转换在一个表达式)、③ 不用写 lambda、④ 可生成 list/set/dict/生成器map/filter 仍有优势的场景① 现成函数(map(str, nums) 不用 lambda)、② 惰性省内存(map/filter 是迭代器、大数据用生成器表达式也惰性)、③ 函数式风格代码库惰性对比map(f, it) 惰性、[f(x) for x in it] 即时列表占内存、(f(x) for x in it) 生成器表达式惰性(等价 map)。理解「推导式更 Pythonic(直观/一体/不写 lambda/可生成各种);map 现成函数时简洁、惰性省内存;大数据用生成器表达式;一般转换筛选用推导式」,就掌握了这个对比。

五、reduce 的替代方案

理解用内置替代 reduce:

reduce 的更好替代(能用就别用 reduce):

求和:
  reduce(lambda a,b: a+b, nums)  →  sum(nums)

连乘:
  reduce(lambda a,b: a*b, nums)  →  math.prod(nums)  # 3.8+

最大/最小:
  reduce(max, nums)  →  max(nums)
  reduce(min, nums)  →  min(nums)

字符串拼接:
  reduce(lambda a,b: a+b, strs)  →  "".join(strs)
  ⚠️ reduce 拼字符串是 O(n²)(每次新建),join 是 O(n)

累积中间结果(要每一步的结果):
  itertools.accumulate(nums)          # 前缀和 [1,3,6,10]
  itertools.accumulate(nums, func)    # 自定义累积
  → reduce 只给最终值、accumulate 给每步

集合并/交:
  reduce(operator.or_, sets)   →  set().union(*sets)
  reduce(operator.and_, sets)  →  ...intersection

为什么优先内置:
  ① 有名字(sum/max/join 一看就懂)
  ② 更快(C 实现)
  ③ 更安全(join 避免 O(n²)、sum 有优化)

reduce 真正的用武之地:
  自定义的、没有现成内置的折叠逻辑
  reduce(merge_func, items, initial)

所以 reduce 用 sum/max/prod/join/accumulate 替代(有名字+更快+更安全)

reduce 的更好替代(能用就别用 reduce):求和 sum(nums)、连乘 math.prod(nums)(3.8+)、最大/最小 max/min(nums)、字符串拼接 "".join(strs)reduce 拼字符串是 O(n²)、join 是 O(n))、累积中间结果 itertools.accumulate(nums)(reduce 只给最终值、accumulate 给每步)、集合并/交 set().union(*sets)为什么优先内置① 有名字(sum/max/join 一看就懂)、② 更快(C 实现)、③ 更安全(join 避免 O(n²))reduce 真正的用武之地:自定义的、没有现成内置的折叠逻辑。理解「reduce 替代:求和 sum、连乘 prod、极值 max/min、拼接 join(避免 O(n²))、累积 accumulate(给每步)、集合 union;内置有名字+更快+更安全;reduce 只用于自定义折叠」,就掌握了 reduce 的替代。

六、总结与实践

总结三个函数:

三件套:
  map(f, it)    映射:每个元素应用 f
  filter(f, it) 过滤:保留 f 为真的
  reduce(f, it) 归约:折叠成一个值(在 functools 里)

返回值:
  map/filter → 惰性迭代器(省内存、只遍历一次)
  reduce → 单个值

实践建议:
  ① 转换/筛选优先用列表推导式(或生成器表达式)
     [f(x) for x in it if cond]
  ② map 仅在"现成函数"时用(map(str, nums))
  ③ reduce 尽量避免——用 sum/max/min/prod/join/accumulate
  ④ 大数据用生成器表达式/map/filter(惰性省内存)

惰性提醒:
  map/filter 只能遍历一次(要复用先 list())

vs 推导式对照:
  map(f, it)         → [f(x) for x in it]
  filter(f, it)      → [x for x in it if f(x)]
  map(f, filter(g,it)) → [f(x) for x in it if g(x)]

核心总结:
  map 映射/filter 过滤/reduce 归约
  map/filter 惰性迭代器、reduce 在 functools
  多数场景用列表推导式更 Pythonic
  reduce 能用 sum/max/join 就别用

所以 map/filter/reduce 三件套,推导式更 Pythonic,reduce 用 sum/join 替代

三件套map(映射)、filter(过滤)、reduce(归约、在 functools 里)。返回值:map/filter 惰性迭代器、reduce 单个值。实践建议① 转换/筛选优先列表推导式(或生成器表达式)、② map 仅在现成函数时用、③ reduce 尽量避免(用 sum/max/prod/join/accumulate)、④ 大数据用生成器表达式/map/filter 惰性省内存惰性提醒:map/filter 只能遍历一次(复用先 list)。理解「map 映射/filter 过滤/reduce 归约;map/filter 惰性、reduce 在 functools;多数用列表推导式更 Pythonic;reduce 用 sum/max/join 替代;大数据用生成器表达式」,就掌握了总结与实践。

记忆钩子:「map/filter/reduce 是函数式三件套:①map(f,it)把 f 作用到每个元素(映射/转换),多个可迭代并行取(以最短为准)②filter(f,it)保留 f 返回真的元素(过滤),★filter(None,it)去除所有假值(0/”/None/[])③reduce(f,it,init)用二元函数从左往右折叠成一个值(reduce(lambda a,b:a+b,[1,2,3,4])=((1+2)+3)+4=10,带 init 从 init 开始、空序列返 init);★关键:map/filter 在 Python 3 返回『惰性迭代器』(不是列表、省内存、只能遍历一次,复用先 list()),reduce 在 functools 里(Python 3 从内置移走,Guido 嫌可读性差);★和列表推导式比:多数场景推导式更 Pythonic([f(x)for x in it]比 map(lambda)直观、[x for x in it if c]比 filter 清晰、筛选+转换一行搞定),但 map 用『现成函数』时更简洁(map(str,nums)不用写 lambda)、且惰性省内存;★reduce 尽量避免——能用 sum(求和)/max·min(极值)/math.prod(连乘 3.8+)/”.join(拼接,避免 reduce 的 O(n²))/itertools.accumulate(要每步中间结果)就别用,reduce 只留给自定义折叠逻辑」

七、常见误区与追问

  • 误区:map(f, it) 返回一个列表。 在 Python 3 返回的是惰性迭代器(map 对象),不是列表;map(str, [1,2,3]) 打印是 <map object>,要列表得 list(map(str, [1,2,3]));这是 Python 2 到 3 的变化(Py2 的 map/filter 返回列表);惰性迭代器省内存(大数据友好)但只能遍历一次(遍历完就空)、也不能索引。
  • 误区:reduce 是内置函数,直接就能用。 在 Python 3 里 reduce 被移到了 functools 模块,要 from functools import reduce 才能用(Python 2 里它才是内置);Guido 当年主张移走它、认为它可读性差(要在脑子里模拟折叠过程),鼓励用有名字的替代(sum、max、join 等);所以看到直接用 reduce 而没 import 的代码,多半是 Python 2 遗留或会报 NameError。
  • 误区:用 reduce 拼接字符串和用 join 一样。 性能差很多——reduce(lambda a,b: a+b, strs) 每次 a+b 都创建一个新字符串(字符串不可变),总复杂度 O(n²);"".join(strs) 先算总长度、一次性分配、O(n);所以拼接字符串永远用 join,不要用 reduce 或循环 +=(同样 O(n²) 风险);这也是「能用专门的内置就别用 reduce」的典型例子。
  • 误区:map 和 filter 只能配合 lambda 使用。 不是——map/filter 接受任何可调用对象,用「现成的函数/内置函数」时反而是它们最简洁的场景:map(str, nums)map(int, str_list)map(len, lists)filter(str.isdigit, chars)filter(None, it)(去假值)都不用写 lambda;反过来,如果需要写 lambda(map(lambda x: x*2, nums)),那还不如用列表推导式 [x*2 for x in nums] 直观。
  • 追问:map/filter/reduce 和列表推导式相比,什么时候该用哪个? 一般的转换和筛选优先用列表推导式(或生成器表达式)——[f(x) for x in it][x for x in it if cond][f(x) for x in it if g(x)],因为它更直观、能把筛选和转换写在一个表达式里、不用写 lambda、还能统一生成 list/set/dict/生成器;map 只在「映射函数是现成函数/内置」时更简洁(map(str, nums));filter 基本都能被推导式替代(filter(None, it)[x for x in it if x]);reduce 尽量避免、用 sum/max/min/prod/join/accumulate 这些有名字的内置;需要惰性省内存时用生成器表达式 (f(x) for x in it) 或 map/filter(它们本身惰性)。
  • 追问:reduce 的折叠过程是怎样的?带初始值有什么用? 不带初始值时,reduce(f, [a,b,c,d]) = f(f(f(a,b),c),d)——先用前两个元素调 f 得到累积值,然后每一步把「当前累积值」和「下一个元素」喂给 f,从左往右直到结束,返回最终累积值;比如 reduce(lambda acc,x: acc+x, [1,2,3,4]) 依次算 1+2=3、3+3=6、6+4=10。带初始值 reduce(f, it, init) = f(f(f(init,a),b),c),从 init 开始折叠,有两个好处:① 空序列时返回 init(不带初始值时对空序列会抛 TypeError);② 累积值可以和元素是不同类型(比如把一列字典折叠合并成一个字典 reduce(lambda acc,d: {**acc,**d}, dicts, {}),初始值 {} 指定了累积容器的类型);所以处理可能为空的序列、或累积到不同类型的容器时要给初始值。
  • 追问:想要累积过程中「每一步」的结果(不只是最终值)该用什么?itertools.accumulate 而不是 reduce:reduce 只返回最终的折叠值,而 accumulate(iterable[, func]) 返回一个迭代器、产出「每一步的累积结果」——list(itertools.accumulate([1,2,3,4])) 得到 [1,3,6,10](前缀和),accumulate([1,2,3,4], lambda a,b: a*b) 得到 [1,2,6,24](前缀积),还能指定 initial 参数(3.8+);典型用途是前缀和/前缀积、运行最大值(accumulate(nums, max))、累计余额等需要「中间过程」的场景;简单说 reduce 给终点、accumulate 给沿途每个点。

八、加强记忆

mapfilterreduce 是函数式三件套map(f, it) 把 f 作用到每个元素(映射/转换,多个可迭代并行取、以最短为准);filter(f, it) 保留 f 返回真的元素(过滤,filter(None, it) 去除所有假值 0/""/None/[]);reduce(f, it, init) 用二元函数从左往右折叠成一个值(reduce(lambda a,b: a+b, [1,2,3,4]) = ((1+2)+3)+4 = 10,带 init 从 init 开始、空序列返 init)。关键map/filter 在 Python 3 返回「惰性迭代器」(不是列表、省内存、只能遍历一次、复用先 list()),reducefunctools 里(Python 3 从内置移走,Guido 嫌可读性差)。和列表推导式比:多数场景推导式更 Pythonic([f(x) for x in it]map(lambda) 直观、筛选+转换一行搞定),但 map 用「现成函数」时更简洁(map(str, nums) 不用写 lambda)、且惰性省内存。reduce 尽量避免——能用 sum(求和)/max·min(极值)/math.prod(连乘)/"".join(拼接,避免 reduce 的 O(n²))/itertools.accumulate(要每步中间结果)就别用,reduce 只留给自定义折叠。一句话「map 映射/filter 过滤(filter(None)去假值)/reduce 归约折叠;map/filter 返回惰性迭代器只遍历一次、reduce 在 functools;多数用列表推导式更 Pythonic、map 现成函数时简洁;reduce 用 sum/max/join/accumulate 替代」。