Perlの代替として(6)

sort(2)

クラスのオブジェクトのソートは次のようにする。


import std.stream;
import std.cstream;
import std.string;

void main(char[][] arg) {
class cData {
int rank, value;
this(int a, int b) {
rank = a;
value = b;
}
int opCmp(Object o) {
cData d = cast(cData)o;
return rank - d.rank;
}
}

auto a = [ 3, 11, 2 ];
cData [] b;
b.length = a.length;
for(int i = 0; i < a.length; i++) {
b[i] = new cData(a[i], a[i]);
}
b.sort;

foreach(d; b) {
dout.writefln("%2d %2d", d.rank, d.value);
}
}


出力:
2 2
3 3
11 11

クラスにopCmpという関数が定義されていないといけない。
ふつうに大小を正・負・0で返せばよい。
ただし、引数はObjectクラスで、上のようにキャストして使う。


opCmpが定義されていれば、比較演算子が使える。


auto a = [ 2, 11, 3 ];
cData [] b;
b.length = a.length;
for(int i = 0; i < a.length; i++) {
b[i] = new cData(a[i], a[i]);
}

dout.writefln(b[0] > b[2]); // false

使えるのは >,>=,<=,< で、
== を使えるようにするには、同様にopEqualsを定義する。


int opEquals(Object o) {
cData d = cast(cData)o;
return rank == d.rank;
}


Cのライブラリのqsortを使えば比較関数を引数に取れるようだが、
よくわからなかった。


実は、ソートについて考えるときに、
与えられた配列に対して順位をつけるという例を考えていた。
すなわち、


3
11
2

という配列があれば、


2. 3
3. 11
1. 2

というものだった。
Perlでは、こんなカンジ。


@a = ( 3, 11, 2 );
@a = map { [ $_, $a[$_] ] } 0..$#a; # ラベルをつける
@a = sort { $a->[1] <=> $b->[1] } @a; # 値でソート
@a = map { [ $_ + 1, @{$a[$_]} ] } 0..$#a; # 順位をつける
@a = sort { $a->[1] <=> $b->[1] } @a; # ラベルでソート
for(@a) {
print "$_->[0]. $_->[2]\n"; # 順位と値を出力
}

だが、Dではスマートな方法を思いつかなかった。