الفرز الفعال للبيانات نوع الهيكل

كل من جاء إلى Elixir / Erlang من لغات أخرى ، على الأرجح ، لديه بعض التوقعات حول كيفية عمل عوامل المقارنة > ، < ، == ، وما إلى ذلك. يتوقع المرء أن 1 < 2 ، (وهذا هو حقًا لذلك). من حيث المبدأ ، يمكننا القول أن المقارنة تعمل كما ينبغي. لكن ليس دائما.


في Elixir / Erlang ، يمكنك مقارنة أي شيء. عموما. بينما بالنسبة لمعاملين من نفس النوع ، فإن النتيجة ليست مثبطة ، كما في المثال أعلاه ، تؤدي مقارنة عاملين من الأنواع المختلفة إلى عواقب غير متوقعة إلى حد ما. لأن الأنواع نفسها "مرتبة للمقارنة". بهذه الطريقة:


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

الأمر الذي يؤدي فجأة إلى حقيقة أن المقارنة المشروعة تمامًا 42 < nil تُرجع إلى الحقيقة.


بالإضافة إلى ذلك ، تتم مقارنة maps (ونتيجة لذلك ، structs ، والتي هي في الواقع نفس maps تحت الغطاء) وفقًا لحقولها بالترتيب الأبجدي. بمعنى آخر ، ستتم مقارنة التواريخ (التي يتم تنفيذها في Elixir بواسطة بنية Date ) بالتتابع بقيم day ، ثم month ، ثم حقول year ، وهذا ، على الأرجح ، ليس كما نود تمامًا.


بدءًا من Enum.sort/2 ، يوفر 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 إما كما هي أو ك tuple {:asc | :desc, StructModule} {:asc | :desc, StructModule} . Enum.sort/2 ذكيًا بما يكفي compare/2 للوحدة النمطية التي تم تمريرها إلى Enum.sort/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/ar482676/


All Articles