我正在尝试编写一个可以将数字列表转换为连续数字列表的函数
例如,转换数字列表,例如:
[1, 2, 3, 4, 10, 11, 12, 20, 21, 30, 32, 42, 43, 44, 45, 48, 49]
进入连续数字列表,如:
[[1, 2, 3, 4], [10, 11, 12], [20, 21], [30], [32], [42, 43, 44, 45], [48, 49]]
也许我在思考这个问题,但我似乎无法在灵药中找到一个好的解决方案。
欣赏正确方向的任何建议或指示。谢谢!
答案 0 :(得分:7)
我可以看到两种方式:使用1.5.0中引入的Enum.chunk_while
或使用手动递归。
以下是使用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)
这是一个副手递归版本:
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
两个版本都做类似的事情。它们迭代列表并将当前元素与前一个元素进行比较。如果当前元素是“下一个”,我们将它推到累加器上,如果不是,我们反转并发出累加器并继续使用新的累加器进行迭代。一旦完成,我们仍然可以在累加器中留下一些东西,如果是这样我们发出最后一个元素。
答案 1 :(得分:5)
虽然已经发布了两个正确答案,但我更倾向于使用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)
输出:
[[1, 2, 3, 4], [10, 11, 12], [20, 21], [30], [32], [42, 43, 44, 45], [48, 49]]
核心思想是:我从一个空列表开始作为累加器。每当一个整数等于累加器+ 1中的最新整数时,我就把它放在同一个列表中,否则我用这个整数创建一个新的列表。最后,累加器需要反转,其中的每个列表也需要反转。
答案 2 :(得分:1)
这是另一种仅使用尾递归的方法,尽管结果相当复杂:
def chunk_cont([hd | rest]) do
do_chunk_cont(rest, hd, [[hd]])
end
defp do_chunk_cont([hd | rest], last, [group | acc_rest]) when hd == last + 1 do
do_chunk_cont(rest, hd, [[hd | group] | acc_rest])
end
defp do_chunk_cont([hd | rest], _last, [group | acc_rest]) do
do_chunk_cont(rest, hd, [[hd] | [Enum.reverse(group) | acc_rest]])
end
defp do_chunk_cont([], _last, [group | acc_rest]) do
[Enum.reverse(group) | acc_rest]
|> Enum.reverse()
end
答案 3 :(得分:1)
另一种解决方案,基于@Dogbert解决方案,但有一些修改
[[1, 2, 3, 4], [10, 11, 12], [20, 21], [30], [32], [42, 43, 44, 45], [48, 49]]
输出:
{{1}}
与@Dogbert解决方案相比的一些优势: