こんにちは。ざわかける!のざわ(@zw_kakeru)です。
C#(およびその他の言語でも)のstringリストの要素を文字数順にソートする方法です。
ラムダ式を使えば簡単に実現できますが、なぜこの書き方でこうなるのか、自分の勉強用に書き記しておきます。
ラムダ式によるstringリストの文字数順ソート
タイトルの通りです。
C#で文字数順ソートを実現するコードは次の通りです。
using System;
public class LambdaSort{
public static void Main(){
var list = new string[] { "USA", "Japan", "Australia", "China" };
Array.Sort(list, (s, t) => t.Length - s.Length);
Console.WriteLine("{0}", string.Join(", ", list));
}
}
これの実行結果は次の通りです。
Australia, Japan, China, USA
listの中身が、文字数が多いものから順にソートできています(同じ文字数の場合は元々の順番のまま)。
解説
実際にソートを行っているのは6行目です。
Array.Sort(list, (s, t) => t.Length - s.Length);
第一引数のlist、これはOK。ソート対象のリストを渡しています。
第二引数でラムダ式を用いた関数が指定されていますが、こちらも何となくは意味が分かります。
list内の任意の2つの要素(sとt)についてt.Length – s.Lengthを行い、その結果が正か負かによって順番を入れ替えるかどうか決定します。
言い換えると、どの2つの要素(sとt)を取ってきてもt.Length – s.Lengthが負になる(sの方がtよりも長い)ようにlist内の要素を並べ替える(ソートする)ということです。
なぜこの記述でソートが実現できるのでしょうか。
まず、C#公式ドキュメントを確認すると、Array.Sortについては次のように書かれています。
Sort(Array, IComparer): 1次元 Array 内の要素を、指定した IComparer を使用して並べ替えます。
IComparerとは何か。さらに公式ドキュメントを追いかけてみると、IComparerとは「2 つのオブジェクトを比較するメソッド」であると書かれています。
すなわちここで指定しているメソッドは、順序を比較するときの比較演算子のような役割を持っているのです。(ここが一番のポイント。)
ソートアルゴリズム自体はシステム側が用意しているものを使います。
それがクイックソートなのかバブルソートなのか知りませんが、ソートという操作は最終的には2つの要素同士の比較を繰り返していくことによって実現されます。
そして、この「2つの要素同士の比較」を行う際に用いられる比較手法(メソッド)こそがIComparerであり、ここで言うラムダ式(s, t) => t.Length – s.Lengthの処理なのです。
結論。
ソート処理は最終的には2つの要素の比較しかやっていなくて、そのときの比較方法をラムダ式で書いている、というのが答えです。
おわりに
私は(s, tってどこから取ってくるんだよ、全部組み合わせを走査するように取ってくるんかな。にしてもそれぞれで引き算して正か負か分かったところでそれをどうソート結果としてまとめるんだ。)と思い込んでいて、このソート記述がずっと謎だったのでようやく解決して良かったです。
長年の疑問が解決して大変スッキリしました。
明日から自信を持って記述することができます。
わかりやすさを優先して結局ラムダ式の説明はほとんどしませんでした。
(本当はdelegateがうんたら、みたいな話があったりするんです。。。)
もっと詳しく知りたい人は各自で調べてください。