はじめに
昨年のアドベントカレンダーにあったGoクイズが正直うらやましく思い、D言語クイズを考えてみました。
D言語を知らない方でも、ソースコードは何となく読める程度の内容なので、これを機会にD言語に興味を持っていただけると幸いです。
クイズの前提
Online D Editor(2021/12/14現在)での実行結果をクイズの正解とさせていただきます。
Online D Editorの環境情報(2021/12/14現在)
import std;
void main()
{
writeln(__VENDOR__);
writeln(__VERSION__);
version(BigEndian)
{
writeln("Big endian byte order ");
}
else version(LittleEndian)
{
writeln("Little endian byte order");
}
}
Digital Mars D
2098
Little endian byte order
ここから問題です。
問題1
ソースコードをコンパイル、実行すると、どのような出力結果になるでしょうか。
import std;
void func1()
{
scope(exit) write("1");
write("2");
throw new Exception("");
}
void main()
{
scope(exit) write("3");
scope(exit) write("4");
write("5");
try
{
func1();
}
catch (Exception e)
{
}
write("6");
}
問題2
ソースコードをコンパイル、実行すると、どのような出力結果になるでしょうか。
import std;
void main()
{
ubyte[] a = [ 0x12, 0x34, 0x56, 0x78 ];
uint[] b = a.to!(uint[]);
writefln("%(%02x %)", cast(uint[])a);
writefln("%(%02x %)", cast(ubyte[])b);
}
問題3
ソースコードをコンパイル、実行すると、どのような出力結果になるでしょうか。
import std;
void func1()
{
write(x);
}
int x = 5;
void main()
{
void func1()
{
write(x+3);
}
int x = 4;
write(x);
write(.x);
func1();
.func1();
}
番外編
答えが一意に決まらないだろうとの理由で、正式な問題ではなく、番外編としました。
ソースコードをコンパイル、実行すると、どのような出力結果になるでしょうか。
あえてdestroy(c)
はコメントアウトしています。
import std;
class C1 {
this(){ write("1"); }
~this(){ write("2"); }
static this(){ write("3"); }
static ~this(){ write("4"); }
}
class C2 : C1 {
this(){ write("5"); }
~this(){ write("6"); }
static this(){ write("7"); }
static ~this(){ write("8"); }
}
void main()
{
C2 c = new C2();
write("9");
// destroy(c);
}
ここから回答編です。ネタばれ注意!!
問題1の答え
問題1の実行結果
521643
答えの補足
-
scope(exit)
は、スコープから抜けるタイミングで実行されます。(3, 4は5, 6よりあとに出力されます) -
scope(exit)
は、例外(exception
)が発生しても、呼び出されます。(1が出力されるのは仕様です) -
scope(exit)
が複数呼び出された場合は、呼び出し順の逆から実行されます。(4→3の順で出力されるのは仕様です)
問題2の答え
問題2の実行結果
78563412
12 00 00 00 34 00 00 00 56 00 00 00 78 00 00 00
答えの補足
-
to
は、実際にデータの変換を行います。元データとは別にデータが生成されます。 - キャスト(
cast
)は、データ変換を行いません。元データを別の型とみなしているだけです。 -
writefln
を使えば、配列の16進数表示も簡潔に書けます。
問題3の答え
問題3の実行結果
4585
答えの補足
- グローバルとローカルに同じ名前の変数や関数を宣言できます。
- グローバル側にアクセスしたい場合は、変数名や関数名の前に
.
をつけます。 -
main
関数内のfunc1
が参照するx
はグローバル側になります。もし、main
関数内のfunc1
よりも先にint x = 4;
が宣言されていれば、ローカル側のx
が参照され、問題3の答えは4575
となっていました。
番外編の答え
番外編の実行結果
371598462
Online D Editor(2021/12/14現在)での実行結果を正解とさせていただきます。
答えの補足
-
static this
は、main
より先に呼び出されます。 - クラスに継承関係がある場合、
static this
も、this
も基底クラスが先に呼び出されます。 - クラスに継承関係がある場合、
static ~this
も、~this
も派生クラスが先に呼び出されます。 -
static ~this
は対象スレッドの終了時に、~this
はオブジェクト削除のタイミングに呼び出されます。どちらが先に実行されるかの規定はなく、この場合、GC(オブジェクト削除)が最後に実行されたということです。 - ちなみに、
destroy
を呼び出せば、~this
がすぐに実行されます。
最後に
普段からD言語を使っている方には、簡単すぎたかもしれません。
問題作成のために、言語仕様をじっくり読んだので、私自身勉強になりました。
第2弾も予定していますので、よろしくお願いします。