Classificação eficaz dos dados do tipo Struct

Todo mundo que veio ao Elixir / Erlang de outras línguas provavelmente tem algumas expectativas em relação à forma como os operadores de comparação > , < , == etc. devem funcionar.Poderíamos esperar 1 < 2 , (e isso é realmente então). Em princípio, podemos dizer que a comparação funciona como deveria. Mas nem sempre.


No Elixir / Erlang, você pode comparar qualquer coisa. Geralmente. Enquanto para dois operandos do mesmo tipo, o resultado não é desencorajador, como no exemplo acima, a comparação de dois operandos de tipos diferentes leva a consequências bastante inesperadas. Porque os próprios tipos são "ordenados para comparação". Desta forma:


 number < atom < reference < function < port < pid < tuple < map < list < bitstring 

O que de repente leva ao fato de que a comparação completamente legítima 42 < nil retorna true .


Além disso, maps (e, como resultado, structs , que são na verdade os mesmos maps sob o capô) são comparados de acordo com seus campos em ordem alfabética. Em outras palavras, as datas (que são implementadas no Elixir pela estrutura Date ) serão comparadas sequencialmente pelos valores do day , month e year e year , e isso provavelmente não é exatamente o que gostaríamos.


A partir da v1.10.0 , o Elixir fornece uma classificação conveniente com o padrão Enum.sort/2 se a estrutura exportar a função 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"}] 

Qualquer módulo que define uma estrutura e exporta a função compare/2 pode ser passado como o segundo parâmetro para a chamada para Enum.sort/2 como está ou como uma tupla {:asc | :desc, StructModule} {:asc | :desc, StructModule} . Enum.sort/2 agora Enum.sort/2 inteligente o suficiente para compare/2 módulo passada para o Enum.sort/2 . A seguir, um trecho do módulo Enum que implementa essa funcionalidade específica:


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

Isso permite que você classifique corretamente as datas no exemplo abaixo (graças ao Date.compare/2 existente), bem como qualquer estrutura definida pelo usuário (se exportar compare/2 , é claro), como no exemplo da estrutura de User acima.


 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]] 

Boa classificação!

Source: https://habr.com/ru/post/pt482676/


All Articles