LoginSignup
16
7

More than 1 year has passed since last update.

ネタじゃないD言語

Last updated at Posted at 2019-12-01

まえがき

D言語は、C++やJavaなどと同じくC言語から派生したプログラミング言語の一つです。周りの人にD言語の話をするとネタだと思われたり、検索しようとすると初っ端から「D言語 オワコン」みたいな予測が出てきたりしますが、そんなことはありません。
結構良い言語なので是非使ってみてほしいです。

D言語のいいところ

圧倒的手軽さ

Pure Cを現代で主戦力として使う機会は組み込みとかを除けばほとんど無いと思います。
Web系以外だと以下のような言語を使うことが多いですが、問題点もあります。

  • C++:Cから変わらずポインタマシマシで、文法がカオス状態。
  • Java:ポインタを直接いじることはないが、堅牢な設計のせいで何をするにも冗長になりがち。
  • Python:文法がムカつく。あと遅い。

Pythonについては文法嫌いは完全に主観だったり、速度もライブラリ使うと平気でC並になったりしますが、それでも何となく使いたくないと思ってる人は多いのでは?
D言語を使えばこんな悩みは全部解決です。

  • ポインタを意識しないコーディングが可能だが、必要に応じて扱うことも可能。
  • Javaっぽい文法だが、OOPを気にせず手続き的に書くことも可能。
  • コンパイラ型言語で実行は爆速。

いい感じなマルチパラダイム

D言語は色々な言語のいいとこ取りをしており、自由な書き方ができます。

C++っぽさ

テンプレートも書けるし演算子オーバーロードもできます。
でも多重継承は禁止されたので、潔くinterfaceを使いましょう。

template Hoge(T){ alias T* t; }  //T*型をtとaliasする
Hoge!(int).t var;              //int*型のvarが宣言される

R Cube(R arg){ return arg*arg*arg; }  //関数テンプレート
int foo=Cube(int)(5);
//Cube(5)でもRはintであると推論される(後述)

関数型プログラミング

一般的にクロージャと言われるものは、D言語ではデリゲートと呼ばれます。
関数は、グローバルなものはfunction型、クラスメンバはdelegate型です。
int(*f(char))(int)とか、いちいちSTL呼んだりとかしなくて大丈夫です。

import std.stdio:writeln;

real square(real a){ return a*a; }
real cube  (real a){ return a*a*a; }

real doubleFunc(real function(real) f, real var){
    return 2*f(var);
}

doubleFunc(&square, 3.0).writeln;  //表示:18.0
doubleFunc(&cube, 3.0).writeln;    //表示:54.0

ラムダ式も書けます。以下はどれも同じ意味です。

real function(real) square = (real a) {
    return a*a;
};
//関数リテラル側の引数の型は省略できる
//型推論(後述)で宣言する場合はムリ
real function(real) square = (a){
    return a*a;
};

real function(real) square = (real a) => a*a;
real function(real) square = a => a*a;

2020/5/14追記

functiondelegateの区別がよくわかってなかったんですが、つまるところ関数リテラルについては
自身と同じスコープ内の変数にアクセスするものdelegateそれ以外はfunctionと考えてよいかと思います。
したがって、

int global;

void hoge(){
    int local;
    typeof(() => global).stringof.writeln;
        //int function() nothrow @nogc @safe
    typeof(() => local).stringof.writeln;
        //int delegate() pure nothrow @nogc @safe
}

となります。
またリテラルでない一般の関数の参照は、グローバル関数はfunctionです(当然ですが)。
スコープ内関数(すなわち関数内のローカル関数やクラス内のメンバ関数)は、たとえpureであってもdelegateとなります。

auto gf(){ return 1; }

class A{
    int member;
    int mf1(){ return 1; }
    int mf2(){ return member; }
}

void fuga(){
    int local
    int lf1(){ return 1; }
    int lf2(){ return local; }
    A a = new A;

    typeof(&gf).stringof.writeln;
        //int function() pure nothrow @nogc @safe
    typeof(&lf1).stringof.writeln;
        //int delegate() pure nothrow @nogc @safe
    typeof(&lf2).stringof.writeln;
        //int delegate() pure nothrow @nogc @safe
    typeof(&a.mf1).stringof.writeln;
        //int delegate()
    typeof(&a.mf2).stringof.writeln;
        //int delegate()
}

型推論

強い静的型付けですが、型推論が使えます。キーワードautoはその型を自動で設定します。

import std;

auto str = readln.split(",");
//キーボードから1行読み込み、","で区切ったものがstring型の配列になる

auto func(){
    return str[0].to!(int);
}
//int型が返る(当然文字列から変換できなかったら例外)

auto area = (real r) => r*r*3.14;

多少楽?
ちなみにareaの型はreal function(real r) pure nothrow @nogc @safeとなります。

配列

動的配列が言語仕様でサポート。
境界チェックもやってくれるし、メモリ手動確保とかしなくても大丈夫です。

int[] array = [403,512,334];
//配列作成時に要素数を付けないと動的配列になる
auto a = array[2];
//auto b = array[-1];
//auto c = array[3];    境界チェックで無効な添字はエラー

auto len = array.length;
array.length++;
array[3] = 365;
//プロパティ"length"は要素数を指す
//lengthを操作すると要素数が増減できる

array[] += 100;  //全要素に100を加算する

int array2[] = [10,251];
array ~= array2; //arrayの末尾にarray2を結合
array ~= 252     //要素を直接結合することも可能

連想配列もあります。

int[string] age = ["Yamada":54, "Tanaka":32, "Sato":45];
auto sato = age["Sato"];
//auto kudo = age["Kudo"];  //存在しない要素なのでやはりエラー

age["Kato"] = 17;

assert("Yamada" in age);
    //key in arrで、arrのキーにkeyが存在するか調べる

そこそこ充実したライブラリ

D言語の現在の標準ライブラリPhobos Runtime Libraryです。
さすがにJavaのようなハウルの動く城型巨大ライブラリではありませんが、システム、入出力、データ構造、時間、正規表現等々基本的な機能は全て揃っています。
また、dubという公式のビルドツール兼パッケージマネージャがあり、これを経由してレポジトリから有志が開発した各種パッケージをインストールできます。

契約プログラミング

D言語の基本理念っぽい何かです。自分もD言語始めるまで知らなかったのですが、

契約プログラミングとは、プログラムコードの中にプログラムが満たすべき仕様についての記述を盛り込む事で設計の安全性を高める技法。 -Wikipediaより抜粋

らしいです。

assertはカッコ内の式がtrueであるように制限します。
in-outは関数が呼ばれた時、処理が終わった時のそれぞれで実行され、関数本来の処理自体はbody内に記述します。

また関数に属性というのを付けることができます。

属性の例   説明
pure 同じ引数に対しては常に同じ結果を返す
nothrow 例外を投げない
@nogc 関数内でGCを呼ぶかもしれない処理をさせない
@safe メモリに関してバッファオーバラン等の未定義動作を起こさない
属性を付けることでそのことを保証し、もし違反していればコンパイラが警告を出します。
import std.math;

//三平方の定理で直角三角形の対辺を求める
real func(real a, real b)
in{
    assert(a>0 && b>0);    //aとbが0以下だと例外を投げる
}body{
    return sqrt(a*a + b*b);
}

D言語の悪いところ

マイナー

これはよく言われることで、何よりも痛いところです。人口が少ないと資料が増えず、始める人が増えないので悪循環なのですが...

仕様がコロコロ変わる

これもよく言われます。そもそもD言語には大きくD1とD2が存在し、D1は2010年を最後に開発が終了しています(サポートも2012年に終了しました)。現在はD2が開発されていて、これから使うとしたらまず間違いなくD2になるでしょう。
D2も2ヶ月に一度のマイナーリリースですが、巷で言われるような破壊的仕様変更はほとんどありません。筆者はD言語歴9ヶ月くらいの甘ちゃんですが、少なくともその範囲では「前は動いたコードがもう動かない!」なんてことはありません。

マスコットがウザい

D-man.png
ウザカワなD言語くん
Gopherよりは全然マシだと思う

おすすめする人

こういう人がD言語を有効活用できるのではないかと思います。

経験豊富な歴戦の勇士

先述の通りD言語は欲張りセットなので、組み合わせ次第で大抵のことがD言語で完結するらしいです。僕のようなヘナチョコ学生よりD言語の真の力を引き出せるかと思います。

C++やJavaにうんざりしてる人

C++はポインタ管理や悪魔文法、Javaはclass主義や広大すぎる標準ライブラリなど、機能面は置いておいたとしても「書いてて嫌になる」ということは多いんじゃないかと思います。D言語は結構自由に書けてC++ほど無法地帯でもなく、そこそこストレスフリーに書けるのでおすすめです。

C言語を勉強し、次にステップアップしたい人

C言語を勉強した後はC++を進められることが多いと思いますが、山に例えるとすれば、Cが小高い山ならC++はエベレストです。いきなりC++に行くのは相当難しいように感じます。
D言語はいわば富士山で、登り切ろうとすると大変ですが5合目くらいでもそこそこ遊べます。文法も非常に近いので移行は簡単です。

総評

better Cを目指しているだけあり、ぱっと見はC++に非常に近く、機能には飛び抜けたところのない普通の言語です。しかし「C++のように柔軟で高速に、Javaのように堅牢に、Pythonのように手軽に」書けるというのは、非常に価値あることではないかと思います。

お勉強のやりかた

D言語ツアーを読む

D言語ツアー (Dlang Tour)は、言語設計者のウォルター・ブライト氏を会長とするD言語財団のホームページ内にある、D言語の入門的解説コーナーです。これを一通り読めば基本的な言語仕様は理解できます

その後

適当に色々書いて、必要に応じてちょっとずつ覚えていくのが一番早いと思います。
プログラミング言語D(日本語訳)が言語仕様やライブラリ等を日本語で記載してくださっているので、基本的なことであれば困ったらここを読めば解決します。
ただやはり時間が立つにつれ更新部分、特に積極的に開発が進められているような部分はズレが大きくなっています。英語ですが、なるべく先述の公式ホームページを読んだほうがいいと思います。
#参考文献

16
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
7