长生不老药:订购没有内置排序功能的数字列表

elixir: order a list of numbers without built-in sort function

如果我有一个每次长度都不同的列表,并且我想将它从最低到最高排序,我该怎么做?

如果我有:[-5, -23, 5, 0, 23, 0.7, -6, 23, 67]

我要:[-23, -6, -5, 0, 0.7, 5, 23, 23, 67]

我尝试使用 for 循环来迭代列表并创建一个新列表来接收数字,但看起来行不通。也许我可以改用 Enum 函数,但我真的对这个没有想法。

如果不能使用Enum.sort,则必须实现自己的排序算法。

Elixir 是一种不可变的语言,因此流行的算法需要就地操作,例如 Quicksort do not work well in Elixir. Here is an implementation of Mergesort:

defmodule Example do
  def merge_sort([]), do: []
  def merge_sort([_] = list), do: list

  def merge_sort(list) when is_list(list) do
    len = length(list)
    {a, b} = Enum.split(list, div(len, 2))
    a = merge_sort(a)
    b = merge_sort(b)
    merge(a, b, [])
  end

  defp merge([], b, acc), do: Enum.reverse(acc) ++ b
  defp merge(a, [], acc), do: Enum.reverse(acc) ++ a

  defp merge([a_head | a_tail] = a, [b_head | b_tail] = b, acc) do
    if a_head <= b_head do
      merge(a_tail, b, [a_head | acc])
    else
      merge(a, b_tail, [b_head | acc])
    end
  end
end

运行:

iex> Example.merge_sort([-5, -23, 5, 0, 23, 0.7, -6, 23, 67])
[-23, -6, -5, 0, 0.7, 5, 23, 23, 67]

只是为了好玩,这里是 reversesplit,如果你根本不想使用 Enum

defp reverse(list), do: reverse(list, [])
defp reverse([], acc), do: acc
defp reverse([h | t], acc), do: reverse(t, [h | acc])

defp split(list, n), do: split(list, [], n)
defp split(a, b, 0), do: {reverse(b), a}
defp split([h | a], b, n), do: split(a, [h | b], n - 1)

在这种情况下,我们甚至可以将 {reverse(b), a} 简化为 {b, a},因为拆分时不需要保留项目顺序,因为结果无论如何都会排序。

下面是一个效率极低但可能是 Enum.split_while/2

最简单的解决方案
Enum.reduce(list, [], fn x, acc ->
  {pre, post} = Enum.split_while(acc, & &1 < x)
  pre ++ [x] ++ post
end)

看起来像作业,所以还有很大的改进空间。作为参考,人们可能会选择任何现有的 sorting algorithms 并实施其中的任何一个。