配列あれこれ

配列のプロパティに max と min がないので不思議に感じた。
sort プロパティがあるのに、である。
sort ができるってぇこた大小比較をしているんだから、配列内の max や min を返すことはできそうに思える。
で、自分で作ってみることを考えると何故だかわかってきた。
int 配列内の最大値を取得する関数は、

int intmax(int[] array) {
  int maxValue = int.min;
  foreach (int value; array) {
    if (maxValue < value) maxValue = value;
  }
  return maxValue;
}

となりますね。
しかし、これを型汎用にしようとすると、必ずしも T.min が定義されているとは限らない(構造体とか)。


が、しかし max 値の初期値がその型が取り得る最小値である必要はなく、配列の値域内であればよい。(どうせ配列内の全要素と比較するわけだから)
で、以下のを作った。

template max(T) {
  T max(T[] array) {
    T maxValue = array[0];
    foreach (T value; array) {
      if (maxValue < value) maxValue = value;
    }
    return maxValue;
  }
}

こんでできるじゃん。opCmp() は全ての型に当てはまるので、これは型汎用である。(function, delegate はどうか知らん)


と思ったら length = 0 の配列はどうなるのだ?
長さ 0 の配列は内部に値がないので、max も min もない。
ものごとにはちゃんと理由があるものだなぁ。
というわけで改良版。

template max(T) {
  T max(T[] array)
    in {
      assert(array != null);
    }
    body{
      T maxValue = array[0];
      foreach (T value; array) {
        if (maxValue < value) maxValue = value;
      }
      return maxValue;
    }
}

契約の部分が、

assert(array.length > 0);

ではないのは、length を 0 に設定すると、自動的に配列も null になっちゃうからである。


ところで、配列に sort プロパティがついているので、max, min はそれぞれ、

  arr.dup.sort[length - 1]
  arr.dup.sort[0]

でも取得できる。
dup しているのは、sort は自分自身を sort しちゃうので。
プロパティを参照しただけで中身が変わっちゃうのはえぇんかいなという気がするが、それはさておき、max, min 値をとるためだけに、dup して sort するのは処理にムダが多いので、上記テンプレートも有用な場面があるかも。


そのデンで行くと、連想配列の key や value の max値もとれるな。

map.keys.dup.sort[0]           
map.keys.dup.sort[length - 1]  
map.values.dup.sort[0]         
map.values.dup.sort[length - 1]

ってな具合に。
...... あり?


sort が配列自身を sort してしまうと、連想配列の keys や values を sort すると、もとの連想配列はどうなるんだろう?
しかしまぁ、連想配列が keys や values を配列の形で持っているわけでなく、必要になったときに配列作ってコピーしてるんだろう。まぁやってみよう。

int[char[ ] ] map;
map["B"] = 100;
map["C"] = 1;
map["A"] = 10;
ToString!(typeof(map))(map).writefln();

writefln(map.keys.sort[0]);
writefln(map.keys.sort[length - 1]);
ToString!(typeof(map))(map).writefln();

writefln(map.values.sort[0]);
writefln(map.values.sort[length - 1]);
ToString!(typeof(map))(map).writefln();
 ...
[(A:10),(B:100),(C:1)]
A
C
[(A:10),(B:100),(C:1)]
1
100
[(A:10),(B:100),(C:1)]

やっぱりねぇ。keys や values がいぢくられても、もとの連想配列に変化なし。
keys や values に値を直接つっこんでみるとどうなるかな?

map.keys[0] = "AAA";
ToString!(typeof(map))(map).writefln();
map.values[2] = 1000;
ToString!(typeof(map))(map).writefln();
...
[(A:10),(B:100),(C:1)]
[(A:10),(B:100),(C:1)]

やっぱり変化なし。
めでたしめでたし。