Dの会 #4 資料
Range, 文字列, 連想配列
順番
- type, qualifier
- array, GC and foreach-loop
- string, UTF
- Associative Array(HashMap)
- function(template)
- Exception, scope(exit|failure|success), DbC, unittest
- revisit function
- revisit template-function
- struct, union
- class, interface,
- re-revisit template
復習-1
Project Euler problem-1より。
10未満の自然数のうち, 3 もしくは 5 の倍数になっているものは 3, 5, 6, 9 の4つがあり, これらの合計は 23 になる.
同じようにして, 1000 未満の 3 か 5 の倍数になっている数字の合計を求めよ.
正解:233168
復習−1−2
以下の関数を使って解いてみる
iota(1, 1000)
filter!"...."
reduce!"...."
[1, 2, 3, 4].filter!"(a & 0x3) == 0" -> [4]
[1, 2, 3, 4].reduce!"a * b" -> 24
復習−2
Project Euler problem-2より。
フィボナッチ数列の項は前の2つの項の和である. 最初の2項を 1, 2 とすれば, 最初の10項は以下の通りである.
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
数列の項の値が400万以下の, 偶数値の項の総和を求めよ.
正解:4613732
復習−2−1
以下の関数を使って解いてみる
recurrence!"...."
until!"...."
filter!"...."
recurrence!"a[n-1] * (n+1)"(1).until!"a > b"(1000) -> [1, 2, 6, 24, 120, 720]
Range
- リストの抽象的表現
auto popHead(R)(ref R r)
{
enforce(!r.empty); // 空か?
auto e = r.front; // 先頭要素
r.popFront(); // 先頭を消す
return e;
}
配列とRange
import std.array;
void main()
{
auto arr = [1, 2, 3, 4];
writeln(arr.empty);
writeln(arr.front);
arr.popFront();
writeln(arr);
writeln(arr.empty);
writeln(arr.front);
arr = [];
writeln(arr.empty);
}
Rangeを作ってみる
- 0, 1, 2, 3, ...
- 無限レンジ
import std.stdio, std.range;
struct InfSeq{
enum bool empty = false; // 終わりなし
int front() @property { return _i; }
void popFront() { ++_i; }
private int _i;
}
void main()
{
writeln(InfSeq().take(100));
}
準備
WindowsでUTF文字列を表示させる
- ターミナルの対応
- 関数のハイジャック
ターミナルの対応
chcp 65001
- フォントを変える
関数ハイジャック
import std.algorithm;
import std.conv, std.format, std.string;
import std.windows.charset;
import std.stdio;
version(Windows)
{
template writeImpl(alias f, string ln)
{ void writeImpl(T...)(auto ref T args)
{ printf(f(forward!args).toMBSz);
printf(ln); }}
alias write = writeImpl!(text, "");
alias writef = writeImpl!(format, "");
alias writeln = writeImpl!(text, "\r\n");
alias writefln = writeImpl!(format, "\r\n");
}
void main()
{
writeln("ああああ");
writef("嗚呼唖々");
}
UTF-32
- 一文字 == 4byte固定
import std.stdio;
void main()
{
// dchar, dstring が UTF-32
dstring[] ss = ["a", "あ", "阿", "\U0001F363"];
foreach(s; ss)
writefln(`%(%s%) => %(%02X%)`, [s], (cast(immutable(ubyte)[])s).dup.reverse);
}
UTF-16
- 基本:1文字 == 2byte
- サロゲートペア(2byte*2で一文字)
import std.stdio;
void main()
{
// wchar, wstring が UTF-32
wstring[] ss = ["a", "あ", "阿", "\U0001F363"];
foreach(s; ss)
writefln(`%(%s%) => %(%02X%)`, [s], (cast(immutable(ubyte)[])s).dup.reverse);
}
UTF-8
- 1~6 byteまでの可変長
import std.stdio;
void main()
{
// char, string が UTF-32
string[] ss = ["a", "あ", "阿", "\U0001F363"];
foreach(s; ss)
writefln(`%(%s%) => %(%02X%)`, [s], (cast(immutable(ubyte)[])s).dup.reverse);
}
問題
- 文字列の先頭文字を得るには?
- ヒント
- immutable(char|wchar|dchar)[]
auto getHead(S)(S str)
{
return str[0];
}
UTF-32の場合
- 1文字は4byte固定
import std.stdio;
void main()
{
dstring str = "トヨッキー";
writeln(str[0]);
}
UTF-8の場合
import std.stdio;
void main()
{
string str = "トヨッキー";
writeln(str[0]); // 文字化け
}
UTF-8:可変長
str[0]
は先頭の1byteだけ- UTF-32に変換してしまう
- Range
import std.array, std.stdio, std.utf;
void main()
{
string str = "トヨッキー";
// UTF-32に変換して、先頭文字だけ返す
writeln(str.toUTF32()[0]); // dchar
// 先頭文字をUTF-32にして返す
writeln(str.front); // dchar
}
UTF-16の場合
- 基本的には1文字は2byte
サロゲートペア:4byteで1文字
- めったにないけど。
UTF-8と同じように処理
文字列のまとめ
- UTF-32に一度変換してしまうのが楽
- 文字列のRangeは、UTF-32のシーケンス
void main()
{
// 推論させると、immutable(char)になる
// dcharにしたいなら、明示的に書く必要がある
foreach(dchar e; "あああ")
writeln(e);
}
文字列の問題
入力
標準入力から一行取得する。
import std.stdio, std.range,
std.conv, std.string;
void main()
{
immutable n = readln() // 1行
.chomp() // 末尾\r\n削除
.to!uint; // 変換
// 入力の各行に対してループ
foreach(line; stdin.byLine)
writeln(line.take(n));
}
連想配列
void main()
{
int[string] aa = ["1" : 1, "2" : 3];
aa["4"] = 5;
if(auto p = "3" in aa)
writeln(*p); // aa["3"]
else
writeln("aa does not have 3");
writeln(aa.length);
aa.remove("4");
assert("4" !in aa);
}
foreach
void main()
{
auto madoMagi = ["mado": 1, "homu": 2,
"saya": 3, "anko": 4, "mami": 5];
foreach(k, v; madoMagi)
writefln("%s : %s", k, v);
writeln();
foreach(k, ref v; madoMagi)
v = 100;
writefln("%-(%s : %s%|\n%)", madoMagi);
}
その他の列挙方法
aa.byKey
:キーのレンジaa.keys
:キーの配列(新たに確保)aa.byValue
:値のレンジaa.values
:値の配列(新たに確保)
ユーザー定義のキー型
- デフォルトではバイナリから算出
- ユーザーがカスタマイズ可能
/// test.d
import std.stdio;
struct MyKey
{
int a;
int b; // これを無視したい
hash_t toHash() const nothrow @safe { return a; }
bool opEquals(ref const MyKey rhs) const
{ return this.a == rhs.a; }
}
void main()
{
int[MyKey] aa;
aa[MyKey(1, 2)] = 12;
aa[MyKey(2, 3)] = 3;
aa[MyKey(3, 4)] = 4;
aa[MyKey(1, 4)] = 8; // rewrite
writeln(aa);
}