从其他语言来到Elixir / Erlang的每个人,最有可能对比较运算符>
, <
, ==
等如何工作都有一些期望。人们希望1 < 2
,(这确实是这样)。 原则上,我们可以说比较是应该的。 但并非总是如此。
在Elixir / Erlang中,您可以比较任何东西。 一般而言。 对于相同类型的两个操作数,结果并不令人沮丧,如上面的示例中所示,比较两个不同类型的操作数会导致非常意外的结果。 因为类型本身是“排序比较”。 通过这种方式:
number < atom < reference < function < port < pid < tuple < map < list < bitstring
这突然导致一个事实,即完全合法的比较42 < nil
返回true
。
此外,还会根据maps
的字段(按字母顺序)比较maps
(以及结果structs
,实际上是相同的引擎盖下的maps
)。 换句话说,日期( 通过 Date
结构在Elixir中实现)将依次通过day
,然后month
,然后year
的值进行比较,这很可能不是我们想要的。
从v1.10.0
开始, Elixir提供了使用标准Enum.sort/2
便利的排序,如果该结构导出了compare/2
函数:
defmodule User do defstruct [:name] def compare(%User{name: n1}, %User{name: n2}) when n1 < n2, do: :lt def compare(%User{name: n1}, %User{name: n2}) when n1 > n2, do: :gt def compare(%User{}, %User{}), do: :eq end users = [ %User{name: "john"}, %User{name: "joe"}, %User{name: "jane"} ] Enum.sort(users, {:asc, User})
任何定义结构并导出compare/2
函数的模块都可以作为第二个参数传递给对Enum.sort/2
的调用,既可以按原样也可以作为元组{:asc | :desc, StructModule}
{:asc | :desc, StructModule}
。 Enum.sort/2
现在足够聪明,可以 compare/2
传递给Enum.sort/2
compare/2
模块 compare/2
。 以下是实现此特定功能的Enum
模块的摘录:
... defp to_sort_fun(module) when is_atom(module), do: &(module.compare(&1, &2) != :gt) defp to_sort_fun({:asc, module}) when is_atom(module), do: &(module.compare(&1, &2) != :gt) defp to_sort_fun({:desc, module}) when is_atom(module), do: &(module.compare(&1, &2) != :lt)
这样,您就可以正确地排序下面示例中的日期(由于现有的Date.compare/2
),以及任何用户定义的结构(当然,如果它导出compare/2
),如上面带有User
结构的示例中所示。
dates = [~D[2019-01-01], ~D[2020-03-02], ~D[2019-06-06]] Enum.sort(dates)
排序好!