长生不老药:订购没有内置排序功能的数字列表
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]
只是为了好玩,这里是 reverse
和 split
,如果你根本不想使用 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 并实施其中的任何一个。
如果我有一个每次长度都不同的列表,并且我想将它从最低到最高排序,我该怎么做?
如果我有:[-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]
只是为了好玩,这里是 reverse
和 split
,如果你根本不想使用 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 并实施其中的任何一个。