Dの会 #2 資料
Contents
- アンケートの結果
- アンケート
- 結果
- 順番
- 様々な型
- スコープ
- デフォルト初期化値(Default Initializer; Type.init)
- ポインタ等の宣言
- type-qualifier
const
immutable
inout
shared
- おまけ
アンケートの結果
アンケート
http://goo.gl/forms/yzRr0qo1Hv
内容は、C++やDを使う上で重要な知識。
結果
Type-Qualifierについて
int const *
と、int * const
の違いが分かる人は半々くらい。immutableについて
immutableの性質を知っている人も半分くらい。 immutableだから、スレッド間で共有しても無問題(スレッドセーフ)であると知っている人は少数。関数の純粋性
純粋性について知っている人は少数。 「副作用」について聞けば、もっと良かったかも。参照渡しと値渡し
みんな知ってた値セマンティクスと参照セマンティクス
誰も知らないコピーコンストラクタ
ほとんどみんな知ってたデストラクタ
全員知ってたムーヴセマンティクス
誰も知らなかったconst T&
で関数に渡す理由
あとから読み返すと質問の文章が悪かった。 一人だけ知ってた。template
について
template
を知っている人は半分。 でも、template
の本質を知っている人はいなかった。 なのに、Template Meta Programming経験者がいた。GCについて
誰も知らなかった。RAIIについて
誰も知らなかった。参照カウントについて
一人だけ知ってた。イテレータ
ほとんどみんな使える。関数ポインタ
さすがに全員知ってた。関数オブジェクト
一人だけ知ってた。ラムダ式
知っている人は半分。クロージャ
一人だけ知ってた。UTFについて
UTF-8, 16, 32があることは知っていても、UTF-8, 16が可変長で、UTF-32が固定長であることを知っている人はいなかったっぽい。連想配列
半分の人は知ってた。タプル
誰も知らなかった。例外
例外のことは、全員知ってた。意外(失礼)。例外安全については、一人だけ日頃から気を付けてる人がいて安心した。
メモリ安全性
一人だけ日頃から気を付けてくれてた。 Buffer Overflowと未初期化変数には気をつけて。仮想関数
みんな知ってた。 継承について聞けばよかったと後悔。実行時型情報(RTTI) 誰も知らなかった。 授業で教えてくれるのだと思ってた。
順番
- 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
様々な型
Dには、次のような型がある。
* void : 特殊
* bool : 真偽値
* 整数型
* byte : 8bitの符号あり(signed)な整数
* ubyte : 8bitの符号なし(unsigned)な整数
* short : 16bitの符号あり整数
* ushort : 16bitの符号なし整数
* int : 32bitの符号あり整数
* uint : 32bitの符号なし整数
* long : 64bitの符号あり整数
* ulong : 64bitの符号なし整数
* cent : 128bitの符号あり整数(将来のために名前だけ付けられてる)
* ucent : 128bitの符号なし整数(将来のために名前だけ付けられてる)
* size_t : ポインタ値が十分に入る大きさの符号なし整数型
32bit環境だと32bit(uint), 64bit環境だと64bit(ulong)
* ptrdiff_t : size_tと同じ大きさの符号あり整数型
* 浮動小数点型
()の中の3つの数字は、(符号部bit数, 指数部bit数, 仮数部bit数)
* float : 32bitの浮動小数点の実数(1, 8, 23)
* double : 64bitの浮動小数点の実数(1, 11, 52)
* real : 64bit以上(システムによって違う)の浮動小数点の実数
* ifloat : 32bitの浮動小数点の虚数
* idouble : 64bitの浮動小数点の虚数
* ireal : 64bit以上(システムによって違う)の浮動小数点の虚数
* cfloat : 32bitの浮動小数点の複素数, 64bit
* cdouble : 64bitの浮動小数点の複素数, 128bit
* creal : 64bit以上(システムによって違う)の浮動小数点の複素数(64bit以上 * 2の大きさ)
* 文字型
* char : UTF-8でエンコードされた文字(8bit)
* wchar : UTF-16でエンコードされた文字(16bit)
* dchar : UTF-32でエンコードされた文字(32bit)
* 配列
* T[N] : 静的配列型
* T[] : 動的配列型(スライス)
* string : 文字列型 immutable(char)[]
* wstring : 文字列型 immutable(wchar)[]
* dstring : 文字列型 immutable(dchar)[]
* V[K] : 連想配列
* T* : ポインタ
* R function(T...) : 関数ポインタ型
* R delegate(T...) : デリゲート型
* ユーザー定義型
* enum : 列挙体
* struct : 構造体(C++でのstruct, classに相当)
* union : 共用体
* class : クラス(Java, C#等のclassに相当)
* interface : インターフェイス(Java、C#等のinterfaceに相当)
スコープ
// Thread Local Storage
// 各スレッド毎に実体が異なる
int g = 0; // TLS
void main()
{
int g = 3;
assert(g == 3);
assert(.g == 3);
}
デフォルト初期化値(Default Initializer; Type.init)
D言語の変数は宣言した際に初期化されます。
その際の値をデフォルト初期化値といいます。
この初期化をしてほしくない場合には、Type iden = void;
というように= void
とします。
import std.stdio;
void main()
{
int a;
writeln(a); // 0; intのデフォルト初期化値は 0
writeln(int.init); // Type.init でデフォルト初期化値を取得できる
int b = void; // 初期化を阻止
writeln(b); // 何が表示されるかわからない
}
ポインタ等の宣言
void main()
{
int* p1, // <= ポインタ
p2; // <= ポインタ
int[] a1, // <= 配列
a2; // <= 配列
int function(int, int)
f1, // <= 関数ポインタ
f2; // <= 関数ポインタ
}
type-qualifier
- qualifier:修飾子
型T
に対して、qualifier(T)
を導入する。
void main()
{
// const int a;
const int a; // const(int) a;でもOK
// int const * b;
const(int)* b;
// int * const c;
// 該当なし
// int const * const * const * d;
const(int**)* d;
}
T * qualifier
が存在しない理由は「使わないから」。
あと、簡単にライブラリで実装可能。
// E * const を実現する
struct ConstPtr(E) {
private E* _ptr;
inout(E*) get() inout @property { return _ptr; }
alias get this;
this(E* p) { _ptr = p; }
this(ConstPtr!E p) { _ptr = p._ptr; }
@disable void opAssign(ConstPtr!E);
}
import std.stdio;
void main()
{
ConstPtr!int a = new int;
// a = new int; // NG
// a = ConstPtr!int(new int); // NG
*a = 12;
writeln(*a); // 12
}
const
const(T)
は、その型の値は読み取り専用であることを示す。
void main()
{
const(int) a = 3;
int[] mtbl = [0, 1, 2, 3];
// T -> const(T)の暗黙変換は常に可能
const(int[]) cnst = mtbl;
// 非constから書き換え可能
mtbl[0] = 3;
assert(cnst[0] == 3);
auto cp = cnst.ptr;
static assert(is(typeof(cp) : const(int*)));
// const(int)の動的配列
const(int)[] cnst2 = cnst;
cnst2.length = 2; // OK
// cnst2[0] = 3; // NG(要素がconst)
}
immutable
immutable(T)
は、その値は生まれてから死ぬまで絶対に書き換わることがないことを示す。
void main()
{
immutable(int) a = 3;
int[] mtbl = [1, 2, 3];
// T -> immutable(T)の暗黙変換は不可能
// immutable(int[]) immt = mtbl;
// unique-expressionなら、
// T -> immutable(T)へ暗黙変換可能
immutable(int[]) immt = mtbl.dup;
assert(immt !is mtbl);
// immutable(int)の動的配列
immutable(int)[] imm2;
// 参照先や長さは変更可能
imm2 = immt;
imm2.length = 1;
}
inout
たとえば、次のメンバ関数では、mutable, const
, immutable
の3つの関数を宣言しなければいけない。
struct Vector(T)
{
@property
{
T* ptr() { return _arr.ptr; }
const(T)* ptr() const { return _arr.ptr; }
immutable(T)* ptr() immutable { return _arr.ptr; }
}
private:
T[] _arr;
}
inout
を導入することで、この冗長性の問題は解消される。
inout(T)
はT
, const(T)
, immutable(T)
のワイルドカード。
関数引数にinout
があったり、inout
メンバ関数の場合のみ、その関数内で使用可能。
struct Vector(T)
{
// inoutで関数を書けば、3種類の関数を書いたことと同義になる
// int* ptr();
// const(int)* ptr() const;
// immutable(int)* ptr() immutable;
inout(T)* ptr() inout @property {
return _arr.ptr;
}
private:
T[] _arr;
}
shared
複数のスレッド間での共有データを示す。
- 並列化なし:14.1 [s]
- 並列化有り: 6.1 [s]
2倍高速化(on 2-Core, 4-Thread)
import core.sync.mutex;
import std.parallelism;
import std.range;
import std.algorithm;
import std.datetime;
import std.stdio;
shared int[] arr;
__gshared Mutex mtx;
shared static this()
{
mtx = new Mutex;
}
void main()
{
StopWatch sw;
sw.start();
arr.reserve(1024 * 64);
foreach(e; parallel(iota(1024 * 64))){
const a = arr,
b = reduce!"a+b"(0, a);
synchronized(mtx)
arr ~= b;
}
sw.stop();
writeln(sw.peek.msecs, "[ms]");
writeln(arr.length);
}
おまけ
parallel
を付加することで、3倍高速化!(on 2-Core, 4-Thread)
import std.parallelism;
import std.datetime;
import std.stdio;
import std.math;
void main()
{
StopWatch sw;
auto logs = new double[128_000_000];
sw.start();
foreach(i, ref elem; parallel(logs))
elem = log(i + 1.0);
sw.stop();
writeln(sw.peek.msecs, "[ms]");
}