有效排序Struct类型数据

从其他语言来到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}) #⇒ [%User{name: "jane"}, # %User{name: "joe"}, # %User{name: "john"}] 

任何定义结构并导出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) # wrong #⇒ [~D[2019-01-01], ~D[2020-03-02], ~D[2019-06-06]] Enum.sort(dates, {:asc, Date}) # correct #⇒ [~D[2019-01-01], ~D[2019-06-06], ~D[2020-03-02]] 

排序好!

Source: https://habr.com/ru/post/zh-CN482676/


All Articles