我正在尝试编写一个函数,可以将数字列表转换为连续数字列表
例如,转换数字列表,例如:
[1, 2, 3, 4, 10, 11, 12, 20, 21, 30, 32, 42, 43, 44, 45, 48, 49]
Run Code Online (Sandbox Code Playgroud)
进入连续数字列表,如:
[[1, 2, 3, 4], [10, 11, 12], [20, 21], [30], [32], [42, 43, 44, 45], [48, 49]]
Run Code Online (Sandbox Code Playgroud)
也许我正在推翻这个,但我似乎无法在灵药中找到一个好的解决方案.
欣赏正确方向的任何建议或指示.谢谢!
我可以看到两种方式:使用Enum.chunk_while1.5.0中引入的方法或使用手动递归.
这是一个使用的版本Enum.chunk_while:
chunk_fun = fn
elem, [] -> {:cont, [elem]}
elem, [prev | _] = acc when prev + 1 == elem -> {:cont, [elem | acc]}
elem, acc -> {:cont, Enum.reverse(acc), [elem]}
end
after_fun = fn
[] -> {:cont, []}
acc -> {:cont, Enum.reverse(acc), []}
end
Enum.chunk_while(list, [], chunk_fun, after_fun)
Run Code Online (Sandbox Code Playgroud)
这是一个手动递归版本:
def chunk_cont([]), do: []
def chunk_cont([elem | list]), do: chunk_cont(list, elem, [])
defp chunk_cont([], elem, acc), do: [Enum.reverse(acc, [elem])]
defp chunk_cont([elem | list], prev, acc) when prev + 1 == elem do
chunk_cont(list, elem, [prev | acc])
end
defp chunk_cont([elem | list], prev, acc) do
[Enum.reverse(acc, [prev]) | chunk_cont(list, elem, [])]
end
Run Code Online (Sandbox Code Playgroud)
两个版本都做类似的事情.它们迭代列表并将当前元素与前一个元素进行比较.如果当前元素是"下一个",我们将它推到累加器上,如果不是,我们反转并发出累加器并继续使用新的累加器进行迭代.一旦完成,我们仍然可以在累加器中留下一些东西,如果是这样我们发出最后一个元素.
虽然已经发布了两个正确的答案,但我更喜欢Enum.reduce/3在可能的情况下使用显式递归,我相信这可能比Enum.chunk_while/4已经发布的基础解决方案稍微优雅一些.
[1, 2, 3, 4, 10, 11, 12, 20, 21, 30, 32, 42, 43, 44, 45, 48, 49]
|> Enum.reduce([], fn
x, [] -> [[x]]
x, [head = [h | _] | tail] when x == h + 1 -> [[x | head] | tail]
x, [head | tail] -> [[x], head | tail]
end)
|> Enum.map(&Enum.reverse/1)
|> Enum.reverse
|> IO.inspect(charlists: :as_integers)
Run Code Online (Sandbox Code Playgroud)
输出:
[[1, 2, 3, 4], [10, 11, 12], [20, 21], [30], [32], [42, 43, 44, 45], [48, 49]]
Run Code Online (Sandbox Code Playgroud)
核心思想是:我从一个空列表开始作为累加器.每当一个整数等于累加器+ 1中的最新整数时,我就把它放在同一个列表中,否则我用这个整数创建一个新的列表.最后,累加器需要反转,其中的每个列表也需要反转.