sort
Elixir
enum

文系でもわかるElixirのEnum.sortの使い方

はじめに

本稿は、文系出身の自分が関数型言語をやるにあたり、「この関数の使い方わかりにくいな!」と思った内容を文系にもなんとなくわかる(理論とか内部のアルゴリズムは気にしない)Tipsとして残すことを目的としています。

誤用、説明不備に関する指摘大歓迎です。
情報工学的にはこう説明すべき!みたいな高尚な話はスルーします。

今回はElixirでListのsortが必要になりEnum.sort()の公式ドキュメントを読んだがよくわからなかったので色々試してみました。

結論

結論としては
Enum.sort(list, fn(x, y) -> 条件式 end)
と書いた場合、
「条件式の結果がfalseの場合に前後(xとyの要素)を入れ替える」
と理解すれば使い方としてはOKかと

色々やった結果

サンプルmapリストの準備

iex(81)> map1 = %{name: "map1", key1: 100, key2: 200}
%{key1: 100, key2: 200, name: "map1"}
iex(82)> map2 = %{name: "map2", key1: 99, key2: 201}
%{key1: 99, key2: 201, name: "map2"}
iex(83)> map3 = %{name: "map3", key1: 101, key2: 199}
%{key1: 101, key2: 199, name: "map3"}
iex(87)> map4 = %{name: "map4", key1: 100, key2: 201}
%{key1: 100, key2: 201, name: "map4"}
iex(90)> list = [map1, map2, map3 ,map4]
[
  %{key1: 100, key2: 200, name: "map1"},
  %{key1: 99, key2: 201, name: "map2"},
  %{key1: 101, key2: 199, name: "map3"},
  %{key1: 100, key2: 201, name: "map4"}
]

ソートしてみる

fn(x,y) -> x.key1 < y.key1 key1で昇順(同じ値の場合は結果がfalseなので並べ替えが起こってしまう)
fn(x,y) -> x.key1 <= y.key1 key1で昇順(同じ値の場合に並べ替えが起こらない)

iex(100)> Enum.sort(list, fn(x,y) -> x.key1 < y.key1  end)
[
  %{key1: 99, key2: 201, name: "map2"},
  %{key1: 100, key2: 201, name: "map4"},
  %{key1: 100, key2: 200, name: "map1"},
  %{key1: 101, key2: 199, name: "map3"}
]
iex(101)> Enum.sort(list, fn(x,y) -> x.key1 <= y.key1  end)
[
  %{key1: 99, key2: 201, name: "map2"},
  %{key1: 100, key2: 200, name: "map1"},
  %{key1: 100, key2: 201, name: "map4"},
  %{key1: 101, key2: 199, name: "map3"}
]

第2ソート条件を指定する

第2ソート順としてnameの降順を指定する

fn(x,y) -> x.key1 < y.key1 or ( x.key1 == y.key1 and x.name >= y.name) end)

iex(111)> Enum.sort(list, fn(x,y) -> x.key1 < y.key1 or ( x.key1 == y.key1 and x.name >= y.name) end)
[
  %{key1: 99, key2: 201, name: "map2"},
  %{key1: 100, key2: 201, name: "map4"},
  %{key1: 100, key2: 200, name: "map1"},
  %{key1: 101, key2: 199, name: "map3"}
]
iex(114)> Enum.sort(list, fn(x,y) -> x.key1 <= y.key1 end)
[
  %{key1: 99, key2: 201, name: "map2"},
  %{key1: 100, key2: 200, name: "map1"},
  %{key1: 100, key2: 201, name: "map4"},
  %{key1: 101, key2: 199, name: "map3"}
]

x.key1 <= y.key1 or ( x.key1 == y.key1 and x.name >= y.name)
としてしまうとx.key1 <= y.key1の時点で結果がtrueになるので第2条件でのソートが行われない

iex(117)> Enum.sort(list, fn(x, y) -> x.key1 <= y.key1 or ( x.key1 == y.key1 and x.name >= y.name) end)
[
  %{key1: 99, key2: 201, name: "map2"},
  %{key1: 100, key2: 200, name: "map1"},
  %{key1: 100, key2: 201, name: "map4"},
  %{key1: 101, key2: 199, name: "map3"}
]