連想配列
int[char [] ] totalTime;
と宣言すると、keyが文字列の連想配列になる。
もちろん、普通に代入できる。
totalTime[str] = 5;
keyがあるかを調べるには次のようにする。
if(str in totalTime) {
...
列挙は次のようにする。
foreach(str; totalTime.keys) {
...
値なら、
foreach(str; totalTime.values) {
...
だいたいPerlと同じように使える。
ただ、この辺には闇があって、たとえば、
char [] str = "a";
char [] str2 = str ~ str; // aa
int[char [] ] a;
a[str2] = 2;
str2[1] = 'b';
foreach(key; a.keys) {
dout.writeLine(key); // ab
dout.writefln(a[key]); // Error: ArrayBoundsError
}
連想配列内ではkeyの実体は持たないってことか。
Dでは、基本的にこういう書き換えはやってはいけないらしい。
書き換えるなら、コピーをしてそちらを書き換える。
正規表現
おなじみの、
/^[a-z]/
みたいな構文は使えなくて、
RegExpオブジェクトをふつうに作って使う。
auto re = new RegExp("([a-z]+) ([0-9]+)");
auto ary = re.match(line);
auto は、型を自動的に判別できるときに使える。
RegExp re = new RegExp("([a-z]+) ([0-9]+)");
char [][] ary = re.match(line);
と同じ。楽だから使うという感じ。
上を1行にまとめると、
auto ary = RegExp("([a-z]+) ([0-9]+)").match(line);
こういうのはダメらしい。
RegExp re = new RegExp("(\w+) (\d+)");
追記
こう書く。
RegExp re = new RegExp("(\\w+) (\\d+)");
matchはJavaScriptと同じで、文字列の配列が返ってくる。
最初の要素はマッチした全体、その後が()で括った部分が返ってくる。
ary[1] => "a"、ary[2] => "100000000"、といった具合。
マッチしなかった場合、長さ0の配列が返る。
結局、書きあがったコードは、
import std.stream;
import std.cstream;
import std.string;
import std.regexp;
import std.conv;
import std.c.stdlib;void main(char [][] arg) {
int[char[] ] totalTime;
int prevTime = -1;
char [] prevWord;
if(arg.length != 2) {
dout.writeLine("usage : time file.");
exit(1);
}
File inp = new File(arg[1], FileMode.In);
foreach(char [] line ; inp) {
auto ary = RegExp("([a-z]+) ([0-9]+)").match(line);
if(ary) {
char [] word = ary[1];
int now = toInt(ary[2]);
if(prevTime >= 0) {
auto rangeName = prevWord ~ "->" ~ word;
totalTime[rangeName] += now - prevTime;
}
prevWord = word.dup;
prevTime = now;
}
}
inp.close();
foreach(range; totalTime.keys.sort) {
dout.writefln("%s %.3f", range, totalTime[range] / 1000.0);
}
}