如何将数字列表转换为连续数字列表

Jos*_*uve 1 elixir

我正在尝试编写一个函数,可以将数字列表转换为连续数字列表

例如,转换数字列表,例如:

[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)

也许我正在推翻这个,但我似乎无法在灵药中找到一个好的解决方案.

欣赏正确方向的任何建议或指示.谢谢!

mic*_*ala 8

我可以看到两种方式:使用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)

两个版本都做类似的事情.它们迭代列表并将当前元素与前一个元素进行比较.如果当前元素是"下一个",我们将它推到累加器上,如果不是,我们反转并发出累加器并继续使用新的累加器进行迭代.一旦完成,我们仍然可以在累加器中留下一些东西,如果是这样我们发出最后一个元素.


Dog*_*ert 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)
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中的最新整数时,我就把它放在同一个列表中,否则我用这个整数创建一个新的列表.最后,累加器需要反转,其中的每个列表也需要反转.