You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Elixir枚举分块:按5的倍数拆分严格递增整数列表

解决Elixir列表按5的倍数分组的问题

你遇到的情况很典型——Enum.chunk_by/2的工作逻辑和你的需求不匹配,所以才会得到不符合预期的拆分结果。咱们先拆解原因,再给出几种可行的实现方案。

为什么原方法达不到预期?

Enum.chunk_by/2是根据相邻元素的函数返回值是否变化来分割列表的。你用的判断函数fn(x) -> rem(x,5) == 0 end会让每个5的倍数单独成组:当遇到5的倍数时返回true,下一个非倍数元素返回false,触发分割;非倍数元素之间返回值都是false,会被分到同一组。这就导致每个5的倍数被单独拆出来,和你想要的“以5的倍数开头,包含到下一个5的倍数前所有元素”的分组逻辑完全相反。

方案一:用Enum.reduce/3手动构建分组

我们可以通过累加器跟踪当前分组,遇到5的倍数就新建分组,否则把元素加到当前最后一个分组里。为了优化效率(避免频繁拼接列表尾部),可以反向累加最后再反转:

list = [300, 301, 304, 305, 306, 309, 310, 311, 312, 313, 314, 315, 316, 317, 319, 320]

result =
  list
  |> Enum.reduce([], fn x, acc ->
    if rem(x, 5) == 0 do
      # 遇到5的倍数,新建分组放到累加器头部(反向存储)
      [[x] | acc]
    else
      # 非5的倍数,加到当前第一个分组的头部
      case acc do
        [] -> [[x]] # 兼容列表开头不是5的倍数的情况
        [current_group | rest] -> [[x | current_group] | rest]
      end
    end
  end)
  # 先反转每个分组,再反转整个结果列表,恢复正确顺序
  |> Enum.map(&Enum.reverse/1)
  |> Enum.reverse()

IO.inspect(result)
# 输出:[[300, 301, 304], [305, 306, 309], [310, 311, 312, 313, 314], [315, 316, 317, 319], [320]]

方案二:递归+Enum.split_while/2(逻辑更直观)

这种方式思路更清晰:每次找到以5的倍数开头的部分,把后面所有非5的倍数都包含进来,然后递归处理剩下的列表:

defmodule Chunker do
  def split_on_five_multiples([]), do: []

  # 当列表头部是5的倍数时,拆分出后续所有非5的倍数组成当前组
  def split_on_five_multiples([head | tail]) when rem(head, 5) == 0 do
    {group_elements, rest} = Enum.split_while(tail, fn x -> rem(x, 5) != 0 end)
    [[head | group_elements] | split_on_five_multiples(rest)]
  end

  # 兼容列表开头不是5的倍数的情况(你的示例里不需要,但代码更健壮)
  def split_on_five_multiples(list) do
    {prefix, rest} = Enum.split_while(list, fn x -> rem(x, 5) != 0 end)
    case rest do
      [] -> [prefix]
      [head | tail] ->
        {group_elements, remaining} = Enum.split_while(tail, fn x -> rem(x, 5) != 0 end)
        [[prefix | [head | group_elements]] |> List.flatten() | split_on_five_multiples(remaining)]
    end
  end
end

list = [300, 301, 304, 305, 306, 309, 310, 311, 312, 313, 314, 315, 316, 317, 319, 320]
IO.inspect(Chunker.split_on_five_multiples(list))
# 输出和预期一致

总结

Enum.chunk_by/2适合基于相邻元素的共同属性分组,而你的需求是基于“起始标记(5的倍数)”来划分连续区间,这需要主动跟踪分组的起始点,所以用reduce或递归的方式更合适。

内容的提问来源于stack exchange,提问作者Thomas Browne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:35:40