D-programming-quick-guide

提供:Dev Guides
移動先:案内検索

Dプログラミング-概要

Dプログラミング言語は、Digital MarsのWalter Brightが開発したオブジェクト指向のマルチパラダイムシステムプログラミング言語です。 その開発は1999年に始まり、2001年に最初にリリースされました。 D(1.0)のメジャーバージョンは2007年にリリースされました。 現在、DのD2バージョンがあります。

DはCスタイルの構文を持つ言語であり、静的型付けを使用します。 DにはCおよびC ++の多くの機能がありますが、Dの一部ではないこれらの言語の機能もいくつかあります。 Dの注目すべき追加には、

  • 単体テスト
  • 真のモジュール
  • ごみ収集
  • ファーストクラスの配列
  • 無料でオープン
  • 連想配列
  • 動的配列
  • 内部クラス
  • クロージャ
  • 無名関数
  • 遅延評価
  • クロージャ

複数のパラダイム

Dは、複数のパラダイムプログラミング言語です。 複数のパラダイムには、

  • 命令的
  • オブジェクト指向
  • メタプログラミング
  • 機能的
  • 同時

import std.stdio;

void main(string[] args) {
   writeln("Hello World!");
}

学習D

Dを学習するときに行う最も重要なことは、概念に集中し、言語の技術的な詳細に迷子にならないことです。

プログラミング言語を学習する目的は、優れたプログラマーになることです。つまり、新しいシステムの設計と実装、および古いシステムの保守をより効果的にすることです。

Dの範囲

Dプログラミングにはいくつかの興味深い機能があり、公式のDプログラミングサイトでは、Dは便利で強力かつ効率的であると主張しています。 Dプログラミングは、C言語がサイズ変更可能な配列や文字列関数などの標準ライブラリの形式で提供するコア言語に多くの機能を追加します。 Dは、中級から上級プログラマ向けの優れた第2言語です。 Dは、C ++で問題を引き起こすことが多いメモリの処理とポインタの管理において優れています。

Dプログラミングは、主に既存のプログラムを変換する新しいプログラムを対象としています。 組み込みのテストと検証を提供し、大規模なチームが数百万行のコードで記述される大規模な新しいプロジェクトに最適です。

Dプログラミング-環境

Dのローカル環境設定

Dプログラミング言語用に環境をセットアップする場合は、コンピューターで次の2つのソフトウェアが利用できる必要があります。(a)テキストエディター、(b)Dコンパイラー。

Dプログラミング用のテキストエディター

これは、プログラムの入力に使用されます。 いくつかのエディターの例には、Windows Notepad、OS Editコマンド、Brief、Epsilon、EMACS、vimまたはviが含まれます。

テキストエディタの名前とバージョンは、オペレーティングシステムによって異なる場合があります。 たとえば、メモ帳はWindowsで使用され、vimまたはviはLinuxまたはUNIXだけでなくWindowsでも使用できます。

エディターで作成するファイルはソースファイルと呼ばれ、プログラムのソースコードが含まれています。 Dプログラムのソースファイルには、拡張子「 .d 」が付いています。

プログラミングを開始する前に、1つのテキストエディターを用意し、コンピュータープログラムを作成し、ファイルに保存し、ビルドして、最後に実行する十分な経験があることを確認してください。

Dコンパイラ

現在のほとんどのD実装は、効率的に実行するためにマシンコードに直接コンパイルされます。

複数のDコンパイラが利用可能であり、以下が含まれています。

  • DMD -Digital Mars Dコンパイラは、Walter Brightによる公式Dコンパイラです。
  • GDC -オープンなDMDコンパイラのソースコードを使用して構築された、GCCバックエンドのフロントエンド。
  • LDC -コンパイラバックエンドとしてLLVMを使用するDMDフロントエンドに基づくコンパイラ。

上記のさまざまなコンパイラは、https://dlang.org/downloadl [Dダウンロード]からダウンロードできます。

Dバージョン2を使用するため、D1をダウンロードしないことをお勧めします。

次のようにhelloWorld.dプログラムを作成しましょう。 選択したプラットフォームで実行する最初のプログラムとしてこれを使用します。

import std.stdio;

void main(string[] args) {
   writeln("Hello World!");
}

次の出力を確認できます。

$ hello world

WindowsでのDのインストール

Windowsのhttp://downloads.dlang.org/releases/2013/dmd-2.064.2.exe [インストーラー]をダウンロードします。

ダウンロードした実行可能ファイルを実行してDをインストールします。これは、画面の指示に従って実行できます。

これで、cdを使用してファイルを含むフォルダーに切り替え、次の手順を使用して、helloWorld.dなどのdファイルを構築して実行できます-

C:\DProgramming> DMD helloWorld.d
C:\DProgramming> helloWorld

次の出力を確認できます。

hello world

C:\ DProgrammingはフォルダーです。サンプルの保存に使用しています。 Dプログラムを保存したフォルダーに変更できます。

Ubuntu/DebianでのDのインストール

debian installerをダウンロードします。

ダウンロードした実行可能ファイルを実行してDをインストールします。これは、画面の指示に従って実行できます。

これで、cdを使用してファイルを含むフォルダーに切り替え、次の手順を使用して、helloWorld.dなどのdファイルを構築して実行できます-

$ dmd helloWorld.d
$ ./helloWorld

次の出力を確認できます。

$ hello world

Mac OS XでのDのインストール

Macをダウンロードhttp://downloads.dlang.org/releases/2013/dmd.2.064.2.dmg[installer]。

ダウンロードした実行可能ファイルを実行してDをインストールします。これは、画面の指示に従って実行できます。

これで、cdを使用してファイルを含むフォルダーに切り替え、次の手順を使用して、helloWorld.dなどのdファイルを構築して実行できます-

$ dmd helloWorld.d
$ ./helloWorld

次の出力を確認できます。

$ hello world

FedoraへのDのインストール

fedora installerをダウンロードします。

ダウンロードした実行可能ファイルを実行してDをインストールします。これは、画面の指示に従って実行できます。

これで、cdを使用してファイルを含むフォルダーに切り替え、次の手順を使用して、helloWorld.dなどのdファイルを構築して実行できます-

$ dmd helloWorld.d
$ ./helloWorld

次の出力を確認できます。

$ hello world

OpenSUSEへのDのインストール

OpenSUSE installerをダウンロードします。

ダウンロードした実行可能ファイルを実行してDをインストールします。これは、画面の指示に従って実行できます。

これで、cdを使用してファイルを含むフォルダーに切り替え、次の手順を使用して、helloWorld.dなどのdファイルを構築して実行できます-

$ dmd helloWorld.d
$ ./helloWorld

次の出力を確認できます。

$ hello world

D IDE

ほとんどの場合、プラグインの形式でDをIDEでサポートしています。 これも、

  • Visual D pluginはVisual Studio 2005-13のプラグインです
  • DDTは、GDBでデバッグするコード補完を提供するEclipseプラグインです。
  • Mono-Dコード補完、dmd/ldc/gdcサポートによるリファクタリング。 GSoC 2012の一部です。
  • Code Blocksは、Dプロジェクトの作成、強調表示、およびデバッグをサポートするマルチプラットフォームIDEです。

Dプログラミング-基本構文

Dは非常に簡単に習得でき、最初のDプログラムの作成を開始できます。

First Dプログラム

簡単なDプログラムを書いてみましょう。 すべてのDファイルの拡張子は.dです。 そのため、次のソースコードをtest.dファイルに配置します。

import std.stdio;

/*My first program in D*/
void main(string[] args) {
   writeln("test!");
}

D環境が正しくセットアップされていると仮定して、プログラミングを実行してみましょう-

$ dmd test.d
$ ./test

次の出力を確認できます。

test

Dプログラミング言語の基本的な構成要素を簡単に理解できるように、Dプログラムの基本構造を見てみましょう。

Dでインポート

再利用可能なプログラムパーツのコレクションであるライブラリは、インポートの助けを借りてプロジェクトで利用できます。 ここでは、基本的なI/O操作を提供する標準ioライブラリをインポートします。 上記のプログラムで使用されるwritelnは、Dの標準ライブラリの関数です。 テキストの行を印刷するために使用されます。 Dのライブラリの内容は、実行する予定のタスクのタイプに基づいたモジュールにグループ化されます。 このプログラムが使用するモジュールはstd.stdioのみで、これはデータの入出力を処理します。

メイン機能

主な機能はプログラムの開始であり、実行順序とプログラムの他のセクションの実行方法を決定します。

Dのトークン

Dプログラムはさまざまなトークンで構成され、トークンはキーワード、識別子、定数、文字列リテラル、またはシンボルのいずれかです。 たとえば、次のD文は4つのトークンで構成されています-

writeln("test!");

個々のトークンは-

writeln (
   "test!"
)
;

コメント

コメントは、Dプログラムでサポートするテキストのようなものであり、コンパイラーによって無視されます。 以下に示すように、複数行コメントは/で始まり、文字/で終了します-

/*My first program in D*/

コメントの先頭に//を使用して単一のコメントを記述します。

//my first program in D

識別子

D識別子は、変数、関数、またはその他のユーザー定義アイテムを識別するために使用される名前です。 識別子は、文字A〜Zまたはa〜zまたはアンダースコア_で始まり、その後に0個以上の文字、アンダースコア、および数字(0〜9)が続きます。

Dでは、識別子内で@、$、%などの句読点文字を使用できません。 Dは、*大文字と小文字を区別*するプログラミング言語です。 したがって、_Manpower_と_manpower_はDの2つの異なる識別子です。 ここに受け入れ可能な識別子のいくつかの例があります-

mohd       zara    abc   move_name  a_123
myname50   _temp   j     a23b9      retVal

キーワード

次のリストは、Dの予約語のいくつかを示しています。 これらの予約語は、定数、変数、またはその他の識別子名として使用できません。

abstract alias align asm
assert auto body bool
byte case cast catch
char class const continue
dchar debug default delegate
deprecated do double else
enum export extern false
final finally float for
foreach function goto if
import in inout int
interface invariant is long
macro mixin module new
null out override package
pragma private protected public
real ref return scope
short static struct super
switch synchronized template this
throw true try typeid
typeof ubyte uint ulong
union unittest ushort version
void wchar while with

Dの空白

コメントが含まれる可能性のある空白のみを含む行は空白行と呼ばれ、Dコンパイラはそれを完全に無視します。

空白は、空白、タブ、改行文字、コメントを説明するためにDで使用される用語です。 空白はステートメントの一部を別の部分から分離し、インタープリターがステートメント内の1つの要素(intなど)がどこで終わり、次の要素が始まるかを識別できるようにします。 したがって、次のステートメントで-

local age

インタープリターがそれらを区別できるようにするには、ローカルと年齢の間に少なくとも1つの空白文字(通常はスペース)が必要です。 一方、次の文では

int fruit = apples + oranges  //get the total fruits

果物と=、または=とリンゴの間には空白文字は必要ありませんが、読みやすくするために空白文字を自由に含めることができます。

Dプログラミング-変数

変数は、プログラムが操作できるストレージ領域に付けられた名前に他なりません。 Dの各変数には特定のタイプがあり、変数のメモリのサイズとレイアウトを決定します。そのメモリ内に保存できる値の範囲。変数に適用できる一連の操作。

変数の名前は、文字、数字、およびアンダースコア文字で構成できます。 文字またはアンダースコアで始まる必要があります。 Dでは大文字と小文字が区別されるため、大文字と小文字は区別されます。 前の章で説明した基本タイプに基づいて、次の基本変数タイプがあります-

Sr.No. Type & Description
1

char

通常、単一のオクテット(1バイト)。 これは整数型です。

2

int

マシンの整数の最も自然なサイズ。

3

float

単精度浮動小数点値。

4

double

倍精度の浮動小数点値。

5

void

タイプがないことを表します。

Dプログラミング言語では、列挙、ポインター、配列、構造、ユニオンなど、さまざまなタイプの変数を定義することもできます。これについては、以降の章で説明します。 この章では、基本的な変数タイプのみを学習します。

Dの変数定義

変数定義は、変数用に作成する場所と量をコンパイラに指示します。 変数の定義は、データ型を指定し、次のようにその型の1つ以上の変数のリストが含まれています-

type variable_list;

ここで、 type は、char、wchar、int、float、double、bool、またはユーザー定義オブジェクトなどを含む有効なDデータ型である必要があり、 variable_list はコンマで区切られた1つ以上の識別子名で構成されます。 いくつかの有効な宣言がここに示されています-

int    i, j, k;
char   c, ch;
float  f, salary;
double d;

int i、j、k; は、変数i、jおよびkを宣言および定義します。これは、タイプintのi、j、およびkという名前の変数を作成するようコンパイラーに指示します。

変数は、宣言で初期化(初期値を割り当て)できます。 初期化子は、等号とそれに続く定数式で構成されます-

type variable_name = value;

extern int d = 3, f = 5;   //declaration of d and f.
int d = 3, f = 5;          //definition and initializing d and f.
byte z = 22;               //definition and initializes z.
char x = 'x';              //the variable x has the value 'x'.

Dで変数が宣言されると、その変数は常に「デフォルト初期化子」に設定されます。これは T.init として手動でアクセスできます。 T は型です(例: int.init )。 整数型のデフォルトの初期化子は0、ブール値のfalse、および浮動小数点数NaNです。

Dの変数宣言

変数宣言は、指定された型と名前を持つ変数が1つ存在することをコンパイラに保証するため、コンパイラは、変数に関する完全な詳細を必要とせずに、さらにコンパイルを進めることができます。 変数宣言はコンパイル時にのみ意味を持ち、コンパイラはプログラムのリンク時に実際の変数宣言を必要とします。

次の例を試してください。変数はプログラムの開始時に宣言されていますが、メイン関数内で定義および初期化されています-

import std.stdio;

int a = 10, b = 10;
int c;
float f;

int main () {
   writeln("Value of a is : ", a);

  /*variable re definition:*/
   int a, b;
   int c;
   float f;

  /*Initialization*/
   a = 30;
   b = 40;
   writeln("Value of a is : ", a);

   c = a + b;
   writeln("Value of c is : ", c);

   f = 70.0/3.0;
   writeln("Value of f is : ", f);
   return 0;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Value of a is : 10
Value of a is : 30
Value of c is : 70
Value of f is : 23.3333

Dの左辺値と右辺値

Dには2種類の式があります-

  • 左辺値-左辺値である式は、割り当ての左辺または右辺のいずれかとして表示される場合があります。
  • rvalue -右辺値である式は、割り当ての右側ではなく左側に表示されます。

変数は左辺値であるため、割り当ての左側に表示される場合があります。 数値リテラルは右辺値であるため、割り当てられず、左側に表示されることはありません。 次の文は有効です-

int g = 20;

しかし、次は有効なステートメントではなく、コンパイル時エラーを生成します-

10 = 20;

Dプログラミング-データ型

Dプログラミング言語では、データ型は、異なる型の変数または関数を宣言するために使用される広範なシステムを指します。 変数のタイプによって、ストレージ内で占めるスペースの量と、保存されたビットパターンの解釈方法が決まります。

Dのタイプは次のように分類することができます-

Sr.No. Types & Description
1

Basic Types

これらは算術型であり、(a)整数、(b)浮動小数点、および(c)文字の3つの型で構成されます。

2

Enumerated types

これらも算術型です。 これらは、プログラム全体で特定の離散整数値のみを割り当てることができる変数を定義するために使用されます。

3

The type void

型指定子_void_は、使用可能な値がないことを示します。

4

Derived types

それらには、(a)ポインター型、(b)配列型、(c)構造体型、(d)ユニオン型、および(e)関数型が含まれます。

配列型と構造体型は、集合型と総称されます。 関数のタイプは、関数の戻り値のタイプを指定します。 次のセクションで基本的なタイプについて説明しますが、他のタイプについては今後の章で説明します。

整数型

次の表は、標準の整数型とそのストレージサイズと値の範囲を示しています-

Type Storage size Value range
bool 1 byte false or true
byte 1 byte -128 to 127
ubyte 1 byte 0 to 255
int 4 bytes -2,147,483,648 to 2,147,483,647
uint 4 bytes 0 to 4,294,967,295
short 2 bytes -32,768 to 32,767
ushort 2 bytes 0 to 65,535
long 8 bytes -9223372036854775808 to 9223372036854775807
ulong 8 bytes 0 to 18446744073709551615

型または変数の正確なサイズを取得するには、 sizeof 演算子を使用できます。 式_type。(sizeof)_は、オブジェクトまたはタイプのストレージサイズをバイト単位で返します。 次の例は、任意のマシンでint型のサイズを取得します-

import std.stdio;

int main() {
   writeln("Length in bytes: ", ulong.sizeof);

   return 0;
}

上記のプログラムをコンパイルして実行すると、次の結果が生成されます-

Length in bytes: 8

浮動小数点型

次の表は、ストレージサイズ、値の範囲、およびそれらの目的を持つ標準的な浮動小数点型に言及しています-

Type Storage size Value range Purpose
float 4 bytes 1.17549e-38 to 3.40282e+38 6 decimal places
double 8 bytes 2.22507e-308 to 1.79769e+308 15 decimal places
real 10 bytes 3.3621e-4932 to 1.18973e+4932 either the largest floating point type that the hardware supports, or double; whichever is larger
ifloat 4 bytes 1.17549e-38i to 3.40282e+38i imaginary value type of float
idouble 8 bytes 2.22507e-308i to 1.79769e+308i imaginary value type of double
ireal 10 bytes 3.3621e-4932 to 1.18973e+4932 imaginary value type of real
cfloat 8 bytes 1.17549e-38+1.17549e-38i to 3.40282e+38+3.40282e+38i complex number type made of two floats
cdouble 16 bytes 2.22507e-308+2.22507e-308i to 1.79769e+308+1.79769e+308i complex number type made of two doubles
creal 20 bytes 3.3621e-4932+3.3621e-4932i to 1.18973e+4932+1.18973e+4932i complex number type made of two reals

次の例は、フロート型とその範囲値によって取得されたストレージスペースを印刷します-

import std.stdio;

int main() {
   writeln("Length in bytes: ", float.sizeof);

   return 0;
}

上記のプログラムをコンパイルして実行すると、Linuxで次の結果が生成されます-

Length in bytes: 4

キャラクタータイプ

次の表は、標準の文字タイプとストレージサイズとその目的を示しています。

Type Storage size Purpose
char 1 byte UTF-8 code unit
wchar 2 bytes UTF-16 code unit
dchar 4 bytes UTF-32 code unit and Unicode code point

次の例は、char型が使用する記憶領域を出力します。

import std.stdio;

int main() {
   writeln("Length in bytes: ", char.sizeof);

   return 0;
}

上記のプログラムをコンパイルして実行すると、次の結果が生成されます-

Length in bytes: 1

ボイドタイプ

voidタイプは、使用可能な値がないことを指定します。 それは2種類の状況で使用されます-

Sr.No. Types & Description
1

Function returns as void

Dには値を返さないさまざまな関数がありますが、それらはvoidを返します。 戻り値のない関数の戻り値の型はvoidです。 たとえば、 void exit(int status);

2

Function arguments as void

Dには、パラメーターを受け入れないさまざまな関数があります。 パラメーターを持たない関数は、voidとして受け入れることができます。 たとえば、 int rand(void);

現時点ではvoidタイプは理解できない可能性があるため、先に進みましょう。これらの概念については、今後の章で説明します。

Dプログラミング-列挙

名前付き定数値の定義には列挙が使用されます。 列挙型は、 enum キーワードを使用して宣言されます。

_enum_構文

列挙型定義の最も単純な形式は次のとおりです-

enum enum_name {
   enumeration list
}

どこで、

  • _enum_name_は、列挙型の名前を指定します。
  • _enumeration list_は、識別子のコンマ区切りリストです。

列挙リスト内の各シンボルは整数値を表し、その前のシンボルよりも1つ大きくなります。 デフォルトでは、最初の列挙シンボルの値は0です。 たとえば-

enum Days { sun, mon, tue, wed, thu, fri, sat };

次の例は、列挙型変数の使用方法を示しています-

import std.stdio;

enum Days { sun, mon, tue, wed, thu, fri, sat };

int main(string[] args) {
   Days day;

   day = Days.mon;
   writefln("Current Day: %d", day);
   writefln("Friday : %d", Days.fri);
   return 0;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Current Day: 1
Friday : 5

上記のプログラムでは、列挙の使用方法を確認できます。 最初に、ユーザー定義の列挙型Daysの_day_という名前の変数を作成します。 次に、ドット演算子を使用して_mon_に設定します。 writeflnメソッドを使用して、保存されているmonの値を出力する必要があります。 タイプも指定する必要があります。 これは整数型であるため、印刷には%dを使用します。

名前付き列挙プロパティ

上記の例では、列挙にDaysという名前を使用しており、名前付き列挙と呼ばれています。 これらの名前付き列挙型には、次のプロパティがあります-

  • Init -列挙の最初の値を初期化します。
  • min -列挙の最小値を返します。
  • max -列挙の最大値を返します。
  • sizeof -列挙用のストレージのサイズを返します。

前の例を変更して、プロパティを使用します。

import std.stdio;

//Initialized sun with value 1
enum Days { sun = 1, mon, tue, wed, thu, fri, sat };

int main(string[] args) {
   writefln("Min : %d", Days.min);
   writefln("Max : %d", Days.max);
   writefln("Size of: %d", Days.sizeof);
   return 0;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Min : 1
Max : 7
Size of: 4

匿名の列挙

名前のない列挙は、匿名列挙と呼ばれます。 *匿名列挙*の例を以下に示します。

import std.stdio;

//Initialized sun with value 1
enum { sun , mon, tue, wed, thu, fri, sat };

int main(string[] args) {
   writefln("Sunday : %d", sun);
   writefln("Monday : %d", mon);
   return 0;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Sunday : 0
Monday : 1

匿名列挙は、名前付き列挙とほぼ同じように機能しますが、max、min、sizeofプロパティはありません。

基本型構文の列挙

基本型を使用した列挙の構文を以下に示します。

enum :baseType {
   enumeration list
}

基本型には、long、int、およびstringが含まれます。 longを使用した例を以下に示します。

import std.stdio;

enum : string {
   A = "hello",
   B = "world",
}

int main(string[] args) {
   writefln("A : %s", A);
   writefln("B : %s", B);

   return 0;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

A : hello
B : world

その他の機能

Dの列挙は、複数の型を持つ列挙の複数の値の初期化などの機能を提供します。 例を以下に示します。

import std.stdio;

enum {
   A = 1.2f, //A is 1.2f of type float
   B,        //B is 2.2f of type float
   int C = 3,//C is 3 of type int
   D         //D is 4 of type int
}

int main(string[] args) {
   writefln("A : %f", A);
   writefln("B : %f", B);
   writefln("C : %d", C);
   writefln("D : %d", D);
   return 0;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

A : 1.200000
B : 2.200000
C : 3
D : 4

Dプログラミング-リテラル

ソースコードの一部としてプログラムに入力される定数値は、*リテラル*と呼ばれます。

リテラルは任意の基本データ型であり、整数数値、浮動小数点数値、文字、文字列、およびブール値に分割できます。

繰り返しますが、リテラルは通常の変数と同様に扱われますが、定義後に値を変更することはできません。

整数リテラル

整数リテラルは、次のタイプのいずれかです-

  • Decimal は通常の数値表現を使用します。最初の数字は8進法を示すために予約されているため、最初の数字を0にすることはできません。これには0は含まれません。
  • Octal は、番号のプレフィックスとして0を使用します。
  • Binary は、プレフィックスとして0bまたは0Bを使用します。
  • Hexadecimal は0xまたは0Xをプレフィックスとして使用します。

整数リテラルには、UとLを組み合わせたサフィックスを付けることもできます。それぞれ、符号なしとロングを表します。 接尾辞は大文字でも小文字でも、任意の順序で指定できます。

サフィックスを使用しない場合、コンパイラーは値の大きさに基づいてint、uint、long、ulongのいずれかを選択します。

ここに整数リテラルのいくつかの例があります-

212        //Legal
215u       //Legal
0xFeeL     //Legal
078        //Illegal: 8 is not an octal digit
032UU      //Illegal: cannot repeat a suffix

以下は、整数リテラルのさまざまなタイプの他の例です-

85        //decimal
0213      //octal
0x4b      //hexadecimal
30        //int
30u       //unsigned int
30l       //long
30ul      //unsigned long
0b001     //binary

浮動小数点リテラル

浮動小数点リテラルは、1.568のように10進法で指定するか、0x91.bcのように16進法で指定できます。

10進法では、文字eまたはEとその後に数字を追加することにより、指数を表すことができます。 たとえば、2.3e4は「2.3×10の4乗」を意味します。 指数の値の前に「+」文字を指定できますが、効果はありません。 たとえば、2.3e4と2.3e + 4は同じです。

指数の値の前に追加された「-」文字は、「10の累乗で除算される」という意味に変わります。 たとえば、2.3e-2は「2.3を10の2乗で除算する」ことを意味します。

16進システムでは、値は0xまたは0Xで始まります。 指数は、eまたはEの代わりにpまたはPで指定されます。 指数は「10の累乗」ではなく、「2の累乗」を意味します。 たとえば、0xabc.defP4のP4は、「abc.de×2の4乗」を意味します。

浮動小数点リテラルの例をいくつか示します-

3.14159      //Legal
314159E-5L   //Legal
510E         //Illegal: incomplete exponent
210f         //Illegal: no decimal or exponent
.e55         //Illegal: missing integer or fraction
0xabc.defP4  //Legal Hexa decimal with exponent
0xabc.defe4  //Legal Hexa decimal without exponent.

デフォルトでは、浮動小数点リテラルの型はdoubleです。 fとFは浮動小数点を意味し、L指定子は実数を意味します。

ブールリテラル

2つのブールリテラルがあり、それらは標準のDキーワードの一部です-

  • trueを表す true の値。
  • falseを表す false の値。

trueの値が1に、falseの値が0に等しいとは考えないでください。

文字リテラル

文字リテラルは単一引用符で囲まれます。

文字リテラルは、プレーン文字(例、 'x')、エスケープシーケンス(例、 '\ t')、ASCII文字(例、 '\ x21')、Unicode文字(例、 '\ u011e')または名前のある文字として(例えば 「\&copy」、「\♥」、「\€」)。

Dには特定の文字があり、バックスラッシュが前に付いている場合は特別な意味を持ち、改行(\ n)またはタブ(\ t)のような表現に使用されます。 ここでは、そのようなエスケープシーケンスコードのいくつかのリストを持っています-

Escape sequence Meaning
\\ \ character
\' ' character
\" " character
\? ? character
\a Alert or bell
\b Backspace
\f Form feed
\n Newline
\r Carriage return
\t Horizontal tab
\v Vertical tab

次の例は、いくつかのエスケープシーケンス文字を示しています-

import std.stdio;

int main(string[] args) {
   writefln("Hello\tWorld%c\n",'\x21');
   writefln("Have a good day%c",'\x21');
   return 0;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Hello   World!

Have a good day!

文字列リテラル

文字列リテラルは二重引用符で囲みます。 文字列には、文字リテラルに類似した文字が含まれます:プレーン文字、エスケープシーケンス、およびユニバーサル文字。

文字列リテラルを使用して長い行を複数の行に分割し、空白を使用してそれらを区切ることができます。

文字列リテラルの例をいくつか示します-

import std.stdio;

int main(string[] args) {
   writeln(q"MY_DELIMITER
      Hello World
      Have a good day
      MY_DELIMITER");

   writefln("Have a good day%c",'\x21');
   auto str = q{int value = 20; ++value;};
   writeln(str);
}

上記の例では、q "MY_DELIMITER MY_DELIMITER"を使用して複数行の文字を表すことができます。 また、D言語ステートメント自体を表すq \ {}を見ることができます。

Dプログラミング-演算子

演算子は、特定の数学的または論理的な操作を実行するようコンパイラーに指示する記号です。 D言語は組み込みの演算子が豊富であり、次の種類の演算子を提供します-

  • 算術演算子
  • 関係演算子
  • 論理演算子
  • ビット演算子
  • 割り当て演算子
  • その他の演算子

この章では、算術、関係、論理、ビット単位、割り当て、およびその他の演算子を1つずつ説明します。

算術演算子

次の表は、D言語でサポートされているすべての算術演算子を示しています。 変数 A が10を保持し、変数 B が20を保持すると仮定します-

リンク:/d_programming/d_programming_arithmetic_operators [例を表示]

Operator Description Example
+ It adds two operands. A + B gives 30
- It subtracts second operand from the first. A - B gives -10
* It multiplies both operands. A* B gives 200
/ It divides numerator by denumerator. B/A gives 2
% It returns remainder of an integer division. B % A gives 0
++ The increment operator increases integer value by one. A++ gives 11
 —  The decrements operator decreases integer value by one. A-- gives 9

関係演算子

次の表は、D言語でサポートされているすべての関係演算子を示しています。 変数 A が10を保持し、変数 B が20を保持すると仮定します-

リンク:/d_programming/d_programming_relational_operators [例を表示]

Operator Description Example
== Checks if the values of two operands are equal or not, if yes then condition becomes true. (A == B) is not true.
!= Checks if the values of two operands are equal or not, if values are not equal then condition becomes true. (A != B) is true.
> Checks if the value of left operand is greater than the value of right operand, if yes then condition becomes true. (A > B) is not true.
< Checks if the value of left operand is less than the value of right operand, if yes then condition becomes true. (A < B) is true.
>= Checks if the value of left operand is greater than or equal to the value of right operand, if yes then condition becomes true. (A >= B) is not true.
Checks if the value of left operand is less than or equal to the value of right operand, if yes then condition becomes true. (A ⇐ B) is true.

論理演算子

次の表は、D言語でサポートされているすべての論理演算子を示しています。 変数 A が1を保持し、変数 B が0を保持すると仮定します-

リンク:/d_programming/d_programming_logical_operators [例を表示]

Operator Description Example
&& It is called Logical AND operator. If both the operands are non-zero, then condition becomes true. (A && B) is false.
It is called Logical OR Operator. If any of the two operands is non-zero, then condition becomes true. (A
B) is true. ! It is called Logical NOT Operator. Use to reverses the logical state of its operand. If a condition is true then Logical NOT operator will make false.

ビット演算子

ビットごとの演算子はビットに対して機能し、ビットごとの操作を実行します。 &、|、および^の真理値表は次のとおりです-

p q p & q p q
p ^ q 0 0 0 0
0 0 1 0 1
1 1 1 1 1
0 1 0 0 1

A = 60であると仮定します。およびB = 13。 バイナリ形式では、次のようになります-

A = 0011 1100

B = 0000 1101

A&B = 0000 1100

A | B = 0011 1101

A ^ B = 0011 0001

〜A = 1100 0011

D言語でサポートされているビット演算子は、次の表にリストされています。 変数Aが60を保持し、変数Bが13を保持すると仮定します-

リンク:/d_programming/d_programming_bitwise_operators [例を表示]

[cols=",,",options="header",]
|===
|Operator |Description |Example |& |Binary AND Operator copies a bit to the result if it exists in both operands. |(A & B) will give 12, Means 0000 1100. || |Binary OR Operator copies a bit if it exists in either operand. |(A | B) gives 61. Means 0011 1101. |^ |Binary XOR Operator copies the bit if it is set in one operand but not both. |(A ^ B) gives 49. Means 0011 0001 |~ |Binary Ones Complement Operator is unary and has the effect of 'flipping' bits. |(~A ) gives -61. Means 1100 0011 in 2's complement form. |<< |Binary Left Shift Operator. The left operands value is moved left by the number of bits specified by the right operand. |A << 2 give 240. Means 1111 0000 |>> |Binary Right Shift Operator. The left operands value is moved right by the number of bits specified by the right operand. |A >> 2 give 15. Means 0000 1111.
|===

=== 割り当て演算子

次の代入演算子はD言語でサポートされています-

リンク:/d_programming/d_programming_assignment_operators [例を表示]

[cols=",,",options="header",]
|===
|Operator |Description |Example |= |It is simple assignment operator. It assigns values from right side operands to left side operand |C = A + B assigns value of A + B into C |+= |It is add AND assignment operator. It adds right operand to the left operand and assign the result to left operand |C += A is equivalent to C = C + A |-= |It is subtract AND assignment operator. It subtracts right operand from the left operand and assign the result to left operand. |C -= A is equivalent to C = C - A |*= |It is multiply AND assignment operator. It multiplies right operand with the left operand and assigns the result to left operand. |C *= A is equivalent to C = C * A |/= |It is divide AND assignment operator. It divides left operand with the right operand and assign the result to left operand. |C/= A is equivalent to C = C/A |%= |It is modulus AND assignment operator. It takes modulus using two operands and assign the result to left operand. |C %= A is equivalent to C = C % A |<<= |It is Left shift AND assignment operator. |C <<= 2 is same as C = C << 2 |>>= |It is Right shift AND assignment operator. |C >>= 2 is same as C = C >> 2 |&= |It is bitwise AND assignment operator. |C &= 2 is same as C = C & 2 |^= |It is bitwise exclusive OR and assignment operator. |C ^= 2 is same as C = C ^ 2 ||= |It is bitwise inclusive OR and assignment operator |C |= 2 is same as C = C | 2
|===

=== Miscillaneous Operators − SizeofおよびTernary

 *sizeof* や*?など、他の重要な演算子はほとんどありません。 :* D言語でサポートされています。

リンク:/d_programming/d_programming_sizeof_operator [例を表示]

[cols=",,",options="header",]
|===
|Operator |Description |Example |sizeof() |Returns the size of an variable. |sizeof(a), where a is integer, returns 4. |& |Returns the address of a variable. |&a; gives actual address of the variable. |* |Pointer to a variable. |*a; gives pointer to a variable. |? : |Conditional Expression |If condition is true then value X: Otherwise value Y.
|===

=== Dの演算子の優先順位

演算子の優先順位は、式内の用語のグループ化を決定します。 これは、式の評価方法に影響します。 特定の演算子は他の演算子よりも優先されます。

たとえば、乗算演算子は加算演算子よりも優先順位が高くなります。

式を考えてみましょう

x = 7 + 3 * 2。

ここで、xには20ではなく13が割り当てられます。 単純な理由は、演算子*の優先順位が+より高いため、最初に3 * 2が計算され、次に結果が7に加算されるからです。

ここでは、優先順位が最も高い演算子が表の上部に表示され、優先順位が最も低い演算子が下部に表示されます。 式内では、優先順位の高い演算子が最初に評価されます。

リンク:/d_programming/d_programming_operators_precedence [例を表示]

[cols=",,",options="header",]
|===
|Category |Operator |Associativity |Postfix |() [] -> . ++ - - |Left to right |Unary |+ - ! ~ ++ - - (type)* & sizeof |Right to left |Multiplicative | */% |Left to right |Additive |+ - |Left to right |Shift |<< >> |Left to right |Relational |< <= > >= |Left to right |Equality |== != |Left to right |Bitwise AND |& |Left to right |Bitwise XOR |^ |Left to right |Bitwise OR || |Left to right |Logical AND |&& |Left to right |Logical OR ||| |Left to right |Conditional |?: |Right to left |Assignment |= += -=* =/= %=>>= <<= &= ^= |= |Right to left |Comma |, |Left to right
|===

Dプログラミング-ループ

コードのブロックを数回実行する必要がある場合があります。 一般に、ステートメントは順番に実行されます。関数の最初のステートメントが最初に実行され、次に2番目のステートメントが実行されます。

プログラミング言語は、より複雑な実行パスを可能にするさまざまな制御構造を提供します。

ループステートメントは、ステートメントまたはステートメントのグループを複数回実行します。 プログラミング言語で主に使用されるループステートメントの次の一般的な形式-

ループアーキテクチャ

Dプログラミング言語は、ループ要件を処理するために次のタイプのループを提供します。 詳細を確認するには、次のリンクをクリックしてください。

Sr.No. Loop Type & Description
1

while loop

特定の条件が真である間、ステートメントまたはステートメントのグループを繰り返します。 ループ本体を実行する前に条件をテストします。

2

for loop

一連のステートメントを複数回実行し、ループ変数を管理するコードを省略します。

3

do…​while loop

whileステートメントと似ていますが、ループ本体の最後で条件をテストします。

4

nested loops

whileループ、forループ、またはdo..whileループ内で1つ以上のループを使用できます。

ループ制御ステートメント

ループ制御ステートメントは、通常のシーケンスから実行を変更します。 実行がスコープを離れると、そのスコープで作成されたすべての自動オブジェクトが破棄されます。

Dは、次の制御ステートメントをサポートしています-

Sr.No. Control Statement & Description
1

break statement

ループまたはスイッチステートメントを終了し、ループまたはスイッチの直後のステートメントに実行を転送します。

2

continue statement

ループがその本体の残りをスキップし、反復する前にその状態をすぐに再テストします。

無限ループ

条件が決して偽にならない場合、ループは無限ループになります。 for ループは伝統的にこの目的に使用されます。 forループを形成する3つの式はいずれも必要ないため、条件式を空のままにして無限ループを作成できます。

import std.stdio;

int main () {

   for( ; ; ) {
      writefln("This loop will run forever.");
   }
   return 0;
}

条件式が存在しない場合、trueと見なされます。 初期化および増分式を使用できますが、Dプログラマーはより一般的にfor(;;)構造を使用して無限ループを示します。

-Ctrl + Cキーを押すと、無限ループを終了できます。

Dプログラミング-決定

意思決定構造には、実行されるステートメントの2つのセットとともに評価される条件が含まれます。 条件がtrueの場合は1つのステートメントセットが実行され、条件がfalseの場合は別のステートメントセットが実行されます。

以下は、ほとんどのプログラミング言語で見られる典型的な意思決定構造の一般的な形式です-

Dの意思決定ステートメント

Dプログラミング言語は、すべての*非ゼロ*および*非ヌル*値を true と見なし、ゼロ*または *null の場合、 false 値と見なされます。

Dプログラミング言語は、以下のタイプの意思決定ステートメントを提供します。

Sr.No. Statement & Description
1

if statement

  • ifステートメント*は、ブール式とそれに続く1つ以上のステートメントで構成されます。
2

if…​else statement

  • ifステートメント*の後にオプションの* elseステートメント*を続けることができます。これはブール式がfalseの場合に実行されます。
3

nested if statements

1つの if または else if ステートメントを別の if または else if ステートメント内で使用できます。

4

switch statement

  • switch* ステートメントを使用すると、変数を値のリストと等しいかどうかをテストできます。
5

nested switch statements

1つの switch ステートメントを別の switch ステートメント内で使用できます。

は? :Dの演算子

条件演算子? :*前の章の *if …​ else ステートメントを置き換えるために使用できます。 次の一般的な形式があります

Exp1 ? Exp2 : Exp3;

Exp1、Exp2、およびExp3は式です。 コロンの使用と配置に注意してください。

aの値 式は次のように決定されます-

  • Exp1が評価されます。 真の場合、Exp2が評価され、?全体の値になります。 式です。
  • Exp1がfalseの場合、Exp3が評価され、その値が式の値になります。

Dプログラミング-関数

この章では、Dプログラミングで使用される関数について説明します。

Dの関数定義

基本的な関数定義は、関数ヘッダーと関数本体で構成されます。

構文

return_type function_name( parameter list ) {
   body of the function
}

ここに関数のすべての部分があります-

  • 戻り値の型-関数は値を返す場合があります。 return_type は、関数が返す値のデータ型です。 一部の関数は、値を返さずに目的の操作を実行します。 この場合、return_typeはキーワード void です。
  • 関数名-これは関数の実際の名前です。 関数名とパラメーターリストは一緒に関数シグネチャを構成します。
  • パラメータ-パラメータはプレースホルダのようなものです。 関数が呼び出されると、パラメーターに値を渡します。 この値は、実パラメーターまたは引数と呼ばれます。 パラメーターリストは、関数のパラメーターのタイプ、順序、および数を参照します。 パラメーターはオプションです。つまり、関数にパラメーターを含めることはできません。
  • 関数本体-関数本体には、関数の動作を定義するステートメントのコレクションが含まれています。

関数を呼び出す

次のように関数を呼び出すことができます-

function_name(parameter_values)

Dの関数タイプ

Dプログラミングは幅広い機能をサポートしており、以下にリストされています。

  • 純粋な機能
  • スロー機能
  • 参照関数
  • 自動機能
  • 可変長関数
  • 入出力関数
  • プロパティ関数

以下に、さまざまな機能について説明します。

純粋な機能

純粋な関数とは、引数を通じてグローバルまたは静的な可変状態保存にアクセスできない関数です。 これにより、純粋な関数は渡されないものを何も変更しないことが保証されているという事実に基づいて最適化を有効にできます。つまり、関数が同じ引数に対して常に同じ結果を返すという保証です)。

import std.stdio;

int x = 10;
immutable int y = 30;
const int* p;

pure int purefunc(int i,const char* q,immutable int *s) {
  //writeln("Simple print");//cannot call impure function 'writeln'

   debug writeln("in foo()");//ok, impure code allowed in debug statement
  //x = i; //error, modifying global state
  //i = x; //error, reading mutable global state
  //i =* p;//error, reading const global state
   i = y;    //ok, reading immutable global state
   auto myvar = new int;    //Can use the new expression:
   return i;
}

void main() {
   writeln("Value returned from pure function : ",purefunc(x,null,null));
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Value returned from pure function : 30

スロー機能

nothrow関数は、クラスExceptionから派生した例外をスローしません。 スロー機能はスロー機能と共変です。

Nothrowは、関数が例外を発行しないことを保証します。

import std.stdio;

int add(int a, int b) nothrow {
  //writeln("adding"); This will fail because writeln may throw
   int result;

   try {
      writeln("adding");//compiles
      result = a + b;
   } catch (Exception error) {//catches all exceptions
   }

   return result;
}

void main() {
   writeln("Added value is ", add(10,20));
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

adding
Added value is 30

参照関数

Ref関数を使用すると、関数を参照によって返すことができます。 これはref関数のパラメーターに似ています。

import std.stdio;

ref int greater(ref int first, ref int second) {
   return (first > second) ? first : second;
}

void main() {
   int a = 1;
   int b = 2;

   greater(a, b) += 10;
   writefln("a: %s, b: %s", a, b);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

a: 1, b: 12

自動機能

自動関数は、任意のタイプの値を返すことができます。 返される型に制限はありません。 自動タイプ関数の簡単な例を以下に示します。

import std.stdio;

auto add(int first, double second) {
   double result = first + second;
   return result;
}

void main() {
   int a = 1;
   double b = 2.5;

   writeln("add(a,b) = ", add(a, b));
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

add(a,b) = 3.5

可変長関数

Variadiac関数は、関数のパラメーターの数が実行時に決定される関数です。 Cには、少なくとも1つのパラメーターを持つという制限があります。 しかし、Dプログラミングでは、そのような制限はありません。 以下に簡単な例を示します。

import std.stdio;
import core.vararg;

void printargs(int x, ...) {
   for (int i = 0; i < _arguments.length; i++) {
      write(_arguments[i]);

      if (_arguments[i] == typeid(int)) {
         int j = va_arg!(int)(_argptr);
         writefln("\t%d", j);
      } else if (_arguments[i] == typeid(long)) {
         long j = va_arg!(long)(_argptr);
         writefln("\t%d", j);
      } else if (_arguments[i] == typeid(double)) {
         double d = va_arg!(double)(_argptr);
         writefln("\t%g", d);
      }
   }
}

void main() {
   printargs(1, 2, 3L, 4.5);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

int 2
long 3
double 4.5

入出力関数

inoutは、パラメーターのタイプと関数の戻りタイプの両方に使用できます。 これは、可変、const、および不変のテンプレートのようなものです。 可変性属性は、パラメーターから推定されます。 つまり、inoutは、推定された可変性属性を戻り値の型に転送します。 可変性がどのように変化するかを示す簡単な例を以下に示します。

import std.stdio;

inout(char)[] qoutedWord(inout(char)[] phrase) {
   return '"' ~ phrase ~ '"';
}

void main() {
   char[] a = "test a".dup;

   a = qoutedWord(a);
   writeln(typeof(qoutedWord(a)).stringof," ", a);

   const(char)[] b = "test b";
   b = qoutedWord(b);
   writeln(typeof(qoutedWord(b)).stringof," ", b);

   immutable(char)[] c = "test c";
   c = qoutedWord(c);
   writeln(typeof(qoutedWord(c)).stringof," ", c);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

char[] "test a"
const(char)[] "test b"
string "test c"

プロパティ関数

プロパティにより、メンバー変数などのメンバー関数を使用できます。 @propertyキーワードを使用します。 プロパティは、要件に基づいて値を返す関連機能にリンクされています。 プロパティの簡単な例を以下に示します。

import std.stdio;

struct Rectangle {
   double width;
   double height;

   double area() const @property {
      return width*height;
   }

   void area(double newArea) @property {
      auto multiplier = newArea/area;
      width *= multiplier;
      writeln("Value set!");
   }
}

void main() {
   auto rectangle = Rectangle(20,10);
   writeln("The area is ", rectangle.area);

   rectangle.area(300);
   writeln("Modified width is ", rectangle.width);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

The area is 200
Value set!
Modified width is 30

Dプログラミング-キャラクター

文字は文字列の構成要素です。 書記体系の記号は文字と呼ばれます。アルファベットの文字、数字、句読点、スペース文字などです。 紛らわしいことに、キャラクター自体の構成要素もキャラクターと呼ばれます。

小文字の a の整数値は97で、数値1の整数値は49です。 これらの値は、ASCIIテーブルが設計されたときの規則によってのみ割り当てられています。

次の表は、標準の文字タイプとそのストレージサイズと目的を示しています。

文字はchar型で表され、256個の異なる値のみを保持できます。 他の言語のchar型に精通している場合、多くの書記体系の記号をサポートするのに十分な大きさではないことを既に知っているかもしれません。

Type Storage size Purpose
char 1 byte UTF-8 code unit
wchar 2 bytes UTF-16 code unit
dchar 4 bytes UTF-32 code unit and Unicode code point

いくつかの便利な文字関数は以下のとおりです-

  • isLower -小文字かどうかを判断しますか?
  • isUpper -大文字かどうかを判断しますか?
  • isAlpha -Unicodeの英数字(通常、文字または数字)を決定しますか?
  • isWhite -空白文字かどうかを判断しますか?
  • toLower -指定された文字の小文字を生成します。
  • toUpper -指定された文字の大文字を生成します。
import std.stdio;
import std.uni;

void main() {
   writeln("Is ğ lowercase? ", isLower('ğ'));
   writeln("Is Ş lowercase? ", isLower('Ş'));

   writeln("Is İ uppercase? ", isUpper('İ'));
   writeln("Is ç uppercase? ", isUpper('ç'));

   writeln("Is z alphanumeric? ",       isAlpha('z'));
   writeln("Is new-line whitespace? ",  isWhite('\n'));

   writeln("Is underline whitespace? ", isWhite('_'));

   writeln("The lowercase of Ğ: ", toLower('Ğ'));
   writeln("The lowercase of İ: ", toLower('İ'));

   writeln("The uppercase of ş: ", toUpper('ş'));
   writeln("The uppercase of ı: ", toUpper('ı'));
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Is ğ lowercase? true
Is Ş lowercase? false
Is İ uppercase? true
Is ç uppercase? false
Is z alphanumeric? true
Is new-line whitespace? true
Is underline whitespace? false
The lowercase of Ğ: ğ
The lowercase of İ: i
The uppercase of ş: Ş
The uppercase of ı: I

Dで文字を読む

以下に示すように、_readf_を使用して文字を読み取ることができます。

readf(" %s", &letter);

DプログラミングはUnicodeをサポートしているため、Unicode文字を読み取るには、2回読み取り、2回書き込む必要があります。 これは、オンラインコンパイラでは機能しません。 以下に例を示します。

import std.stdio;

void main() {
   char firstCode;
   char secondCode;

   write("Please enter a letter: ");
   readf(" %s", &firstCode);
   readf(" %s", &secondCode);

   writeln("The letter that has been read: ", firstCode, secondCode);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Please enter a letter: ğ
The letter that has been read: ğ

Dプログラミング-文字列

Dは、次の2種類の文字列表現を提供します-

  • 文字配列
  • コア言語文字列

文字配列

以下に示すように、2つの形式のいずれかで文字配列を表すことができます。 最初のフォームはサイズを直接提供し、2番目のフォームはdupメソッドを使用して、文字列「おはよう」の書き込み可能なコピーを作成します。

char[9]  greeting1 = "Hello all";
char[] greeting2 = "Good morning".dup;

上記の単純な文字配列形式を使用した簡単な例を次に示します。

import std.stdio;

void main(string[] args) {
   char[9] greeting1 = "Hello all";
   writefln("%s",greeting1);

   char[] greeting2 = "Good morning".dup;
   writefln("%s",greeting2);
}

上記のコードをコンパイルして実行すると、次のような結果が生成されます-

Hello all
Good morning

コア言語文字列

文字列はDのコア言語に組み込まれています。 これらの文字列は、上記の文字配列と相互運用可能です。 次の例は、単純な文字列表現を示しています。

string greeting1 = "Hello all";

import std.stdio;

void main(string[] args) {
   string greeting1 = "Hello all";
   writefln("%s",greeting1);

   char[] greeting2 = "Good morning".dup;
   writefln("%s",greeting2);

   string greeting3 = greeting1;
   writefln("%s",greeting3);
}

上記のコードをコンパイルして実行すると、次のような結果が生成されます-

Hello all
Good morning
Hello all

文字列連結

Dプログラミングの文字列連結では、チルダ(〜)記号を使用します。

import std.stdio;

void main(string[] args) {
   string greeting1 = "Good";
   char[] greeting2 = "morning".dup;

   char[] greeting3 = greeting1~" "~greeting2;
   writefln("%s",greeting3);

   string greeting4 = "morning";
   string greeting5 = greeting1~" "~greeting4;
   writefln("%s",greeting5);
}

上記のコードをコンパイルして実行すると、次のような結果が生成されます-

Good morning
Good morning

ひもの長さ

バイト単位の文字列の長さは、長さ関数を使用して取得できます。

import std.stdio;

void main(string[] args) {
   string greeting1 = "Good";
   writefln("Length of string greeting1 is %d",greeting1.length);

   char[] greeting2 = "morning".dup;
   writefln("Length of string greeting2 is %d",greeting2.length);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Length of string greeting1 is 4
Length of string greeting2 is 7

文字列比較

Dプログラミングでは文字列の比較は非常に簡単です。 文字列の比較には、==、<、および>演算子を使用できます。

import std.stdio;

void main() {
   string s1 = "Hello";
   string s2 = "World";
   string s3 = "World";

   if (s2 == s3) {
      writeln("s2: ",s2," and S3: ",s3, "  are the same!");
   }

   if (s1 < s2) {
      writeln("'", s1, "' comes before '", s2, "'.");
   } else {
      writeln("'", s2, "' comes before '", s1, "'.");
   }
}

上記のコードをコンパイルして実行すると、次のような結果が生成されます-

s2: World and S3: World are the same!
'Hello' comes before 'World'.

文字列を置き換える

string []を使用して文字列を置き換えることができます。

import std.stdio;
import std.string;

void main() {
   char[] s1 = "hello world ".dup;
   char[] s2 = "sample".dup;

   s1[6..12] = s2[0..6];
   writeln(s1);
}

上記のコードをコンパイルして実行すると、次のような結果が生成されます-

hello sample

インデックスメソッド

indexOfおよびlastIndexOfを含む文字列内の部分文字列の場所のインデックスメソッドについて、次の例で説明します。

import std.stdio;
import std.string;

void main() {
   char[] s1 = "hello World ".dup;

   writeln("indexOf of llo in hello is ",std.string.indexOf(s1,"llo"));
   writeln(s1);
   writeln("lastIndexOf of O in hello is " ,std.string.lastIndexOf(s1,"O",CaseSensitive.no));
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

indexOf.of llo in hello is 2
hello World
lastIndexOf of O in hello is 7

取り扱いケース

ケースの変更に使用される方法を次の例に示します。

import std.stdio;
import std.string;

void main() {
   char[] s1 = "hello World ".dup;
   writeln("Capitalized string of s1 is ",capitalize(s1));

   writeln("Uppercase string of s1 is ",toUpper(s1));

   writeln("Lowercase string of s1 is ",toLower(s1));
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Capitalized string of s1 is Hello world
Uppercase string of s1 is HELLO WORLD
Lowercase string of s1 is hello world

文字を制限する

文字列内の文字列の再文字列を次の例に示します。

import std.stdio;
import std.string;

void main() {
   string s = "H123Hello1";

   string result = munch(s, "0123456789H");
   writeln("Restrict trailing characters:",result);

   result = squeeze(s, "0123456789H");
   writeln("Restrict leading characters:",result);

   s = "  Hello World  ";
   writeln("Stripping leading and trailing whitespace:",strip(s));
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Restrict trailing characters:H123H
Restrict leading characters:ello1
Stripping leading and trailing whitespace:Hello World

Dプログラミング-配列

Dプログラミング言語は、 arrays という名前のデータ構造を提供します。このデータ構造には、同じタイプの要素の固定サイズの順次コレクションが格納されます。 配列は、データのコレクションを格納するために使用されます。 多くの場合、配列を同じ型の変数のコレクションと考える方が便利です。

number0、number1、…​、number99などの個々の変数を宣言する代わりに、numbersなどの1つの配列変数を宣言し、numbers [0]、numbers [1]、…​、numbers [99]を使用して表現します個々の変数。 配列内の特定の要素は、インデックスによってアクセスされます。

すべての配列は、連続したメモリ位置で構成されています。 最下位アドレスは最初の要素に対応し、最上位アドレスは最後の要素に対応します。

配列の宣言

Dプログラミング言語で配列を宣言するには、プログラマは次のように配列に必要な要素の型と要素の数を指定します-

type arrayName [ arraySize ];

これは、単一次元配列と呼ばれます。 _arraySize_はゼロより大きい整数定数でなければならず、_type_は任意の有効なDプログラミング言語データ型にすることができます。 たとえば、double型の_balance_という10要素の配列を宣言するには、次のステートメントを使用します-

double balance[10];

配列の初期化

Dプログラミング言語の配列要素は、次のように1つずつまたは単一のステートメントを使用して初期化できます。

double balance[5] = [1000.0, 2.0, 3.4, 17.0, 50.0];

右側の角括弧[]の間の値の数は、角括弧[]の間の配列に対して宣言する要素の数より大きくすることはできません。 次の例では、配列の単一の要素を割り当てます-

配列のサイズを省略すると、初期化を保持するのに十分な大きさの配列が作成されます。 したがって、あなたが書く場合

double balance[] = [1000.0, 2.0, 3.4, 17.0, 50.0];

次に、前の例で作成したのとまったく同じ配列を作成します。

balance[4] = 50.0;

上記のステートメントは、配列の5番目の要素番号に50.0の値を割り当てます。 すべての配列はベースインデックスとも呼ばれる最初の要素のインデックスとして0を持つため、4番目のインデックスを持つ配列は5番目、つまり最後の要素になります。 次の絵の表現は、上記で説明したのと同じ配列を示しています-

配列プレゼンテーション

配列要素へのアクセス

配列名にインデックスを付けることにより、要素にアクセスします。 これは、配列の名前の後に角かっこ内に要素のインデックスを配置することによって行われます。 たとえば-

double salary = balance[9];

上記のステートメントは、配列から10 ^ th ^要素を取得し、値を変数_salary_に割り当てます。 次の例では、宣言、割り当て、および配列へのアクセスを実装しています-

import std.stdio;
void main() {
   int n[ 10 ];//n is an array of 10 integers

  //initialize elements of array n to 0
   for ( int i = 0; i < 10; i++ ) {
      n[ i ] = i + 100;//set element at location i to i + 100
   }

   writeln("Element \t Value");

  //output each array element's value
   for ( int j = 0; j < 10; j++ ) {
      writeln(j," \t ",n[j]);
   }
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Element   Value
0         100
1         101
2         102
3         103
4         104
5         105
6         106
7         107
8         108
9         109

静的配列と動的配列

プログラムの作成中に配列の長さが指定されている場合、その配列は静的配列です。 プログラムの実行中に長さが変化する場合、その配列は動的配列です。

長さを省略すると動的配列になるため、動的配列の定義は固定長配列の定義よりも簡単です-

int[] dynamicArray;

配列のプロパティ

ここに配列のプロパティがあります-

Sr.No. Property & Description
1

.init

静的配列は、リテラルの各要素が配列要素型の.initプロパティである配列リテラルを返します。

2

.sizeof

静的配列は、配列の長さに配列要素ごとのバイト数を掛けた値を返しますが、動的配列は、動的配列参照のサイズを返します。これは、32ビットビルドでは8、64ビットビルドでは16です。

3

.length

静的配列は配列内の要素の数を返し、動的配列は配列内の要素の数を取得/設定するために使用されます。 長さはsize_t型です。

4

.ptr

配列の最初の要素へのポインターを返します。

5

.dup

同じサイズの動的配列を作成し、配列の内容をそこにコピーします。

6

.idup

同じサイズの動的配列を作成し、配列の内容をそこにコピーします。 コピーは不変として入力されます。

7

.reverse

配列内の要素の順序を逆にします。 配列を返します。

8

.sort

配列内の要素の順序を適切にソートします。 配列を返します。

次の例では、配列のさまざまなプロパティを説明します-

import std.stdio;

void main() {
   int n[ 5 ];//n is an array of 5 integers

  //initialize elements of array n to 0
   for ( int i = 0; i < 5; i++ ) {
      n[ i ] = i + 100;//set element at location i to i + 100
   }

   writeln("Initialized value:",n.init);

   writeln("Length: ",n.length);
   writeln("Size of: ",n.sizeof);
   writeln("Pointer:",n.ptr);

   writeln("Duplicate Array: ",n.dup);
   writeln("iDuplicate Array: ",n.idup);

   n = n.reverse.dup;
   writeln("Reversed Array: ",n);

   writeln("Sorted Array: ",n.sort);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Initialized value:[0, 0, 0, 0, 0]

Length: 5
Size of: 20

Pointer:7FFF5A373920
Duplicate Array: [100, 101, 102, 103, 104]
iDuplicate Array: [100, 101, 102, 103, 104]
Reversed Array: [104, 103, 102, 101, 100]
Sorted Array: [100, 101, 102, 103, 104]

Dの多次元配列

Dプログラミングでは、多次元配列を使用できます。 多次元配列宣言の一般的な形式は次のとおりです-

type name[size1][size2]...[sizeN];

次の宣言は、3次元の5を作成します。 10 . 4整数配列-

int threedim[5][10][4];

Dの2次元配列

多次元配列の最も単純な形式は、2次元配列です。 2次元配列は、本質的には1次元配列のリストです。 サイズ[x、y]の2次元整数配列を宣言するには、次のように構文を書きます-

type arrayName [ x ][ y ];

ここで、 type は有効なDプログラミングデータタイプで、 arrayName は有効なDプログラミング識別子です。

ここで、_type_は任意の有効なDプログラミングデータ型であり、_arrayName_は有効なDプログラミング識別子です。

2次元配列は、x個の行とy個の列を持つテーブルと考えることができます。 3行4列を含む2次元配列 a は以下のように表示することができます-

2次元配列

したがって、配列 a のすべての要素は、 a [i] [j] として要素によって識別されます。ここで、 a は配列の名前であり、 i および j は一意に識別する添え字です。 aの各要素。

2次元配列の初期化

多次元配列は、各行に括弧付きの値を指定することにより初期化できます。 次の配列には3つの行があり、各行には4つの列があります。

int a[3][4] = [
   [0, 1, 2, 3] ,  /*initializers for row indexed by 0*/
   [4, 5, 6, 7] ,  /*initializers for row indexed by 1*/
   [8, 9, 10, 11]  /*initializers for row indexed by 2*/
];

目的の行を示すネストされた中括弧はオプションです。 次の初期化は前の例と同等です-

int a[3][4] = [0,1,2,3,4,5,6,7,8,9,10,11];

2次元配列要素へのアクセス

2次元配列の要素には、添え字を使用してアクセスします。これは、配列の行インデックスと列インデックスを意味します。 例えば

int val = a[2][3];

上記のステートメントは、配列の3行目から4番目の要素を取ります。 上記の図で確認できます。

import std.stdio;

void main () {
  //an array with 5 rows and 2 columns.
   int a[5][2] = [ [0,0], [1,2], [2,4], [3,6],[4,8]];

  //output each array element's value
   for ( int i = 0; i < 5; i++ ) for ( int j = 0; j < 2; j++ ) {
      writeln( "a[" , i , "][" , j , "]: ",a[i][j]);
   }
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

a[0][0]: 0
a[0][1]: 0
a[1][0]: 1
a[1][1]: 2
a[2][0]: 2
a[2][1]: 4
a[3][0]: 3
a[3][1]: 6
a[4][0]: 4
a[4][1]: 8

Dの一般的な配列操作

ここに配列で実行されるさまざまな操作があります-

配列スライス

配列の一部を使用することが多く、配列のスライスは非常に役立ちます。 配列スライスの簡単な例を以下に示します。

import std.stdio;

void main () {
  //an array with 5 elements.
   double a[5] = [1000.0, 2.0, 3.4, 17.0, 50.0];
   double[] b;

   b = a[1..3];
   writeln(b);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

[2, 3.4]

配列コピー

配列のコピーも使用します。 配列コピーの簡単な例を以下に示します。

import std.stdio;

void main () {
  //an array with 5 elements.
   double a[5] = [1000.0, 2.0, 3.4, 17.0, 50.0];
   double b[5];
   writeln("Array a:",a);
   writeln("Array b:",b);

   b[] = a;     //the 5 elements of a[5] are copied into b[5]
   writeln("Array b:",b);

   b[] = a[];  //the 5 elements of a[3] are copied into b[5]
   writeln("Array b:",b);

   b[1..2] = a[0..1];//same as b[1] = a[0]
   writeln("Array b:",b);

   b[0..2] = a[1..3];//same as b[0] = a[1], b[1] = a[2]
   writeln("Array b:",b);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Array a:[1000, 2, 3.4, 17, 50]
Array b:[nan, nan, nan, nan, nan]
Array b:[1000, 2, 3.4, 17, 50]
Array b:[1000, 2, 3.4, 17, 50]
Array b:[1000, 1000, 3.4, 17, 50]
Array b:[2, 3.4, 3.4, 17, 50]

配列設定

配列に値を設定する簡単な例を以下に示します。

import std.stdio;

void main () {
  //an array with 5 elements.
   double a[5];
   a[] = 5;
   writeln("Array a:",a);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Array a:[5, 5, 5, 5, 5]

配列連結

2つの配列を連結する簡単な例を以下に示します。

import std.stdio;

void main () {
  //an array with 5 elements.
   double a[5] = 5;
   double b[5] = 10;
   double [] c;
   c = a~b;
   writeln("Array c: ",c);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Array c: [5, 5, 5, 5, 5, 10, 10, 10, 10, 10]

Dプログラミング-連想配列

連想配列には、必ずしも整数ではないインデックスがあり、まばらに入力できます。 連想配列のインデックスは Key と呼ばれ、そのタイプは KeyType と呼ばれます。

連想配列は、配列宣言の[]内にKeyTypeを配置することで宣言されます。 連想配列の簡単な例を以下に示します。

import std.stdio;

void main () {
   int[string] e;     //associative array b of ints that are

   e["test"] = 3;
   writeln(e["test"]);

   string[string] f;

   f["test"] = "Tuts";
   writeln(f["test"]);

   writeln(f);

   f.remove("test");
   writeln(f);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

3
Tuts
["test":"Tuts"]
[]

連想配列の初期化

連想配列の簡単な初期化を以下に示します。

import std.stdio;

void main () {
   int[string] days =
      [ "Monday" : 0,
         "Tuesday" : 1,
         "Wednesday" : 2,
         "Thursday" : 3,
         "Friday" : 4,
         "Saturday" : 5,
         "Sunday" : 6 ];
   writeln(days["Tuesday"]);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

1

連想配列のプロパティ

連想配列のプロパティは次のとおりです-

Sr.No. Property & Description
1

.sizeof

連想配列への参照のサイズを返します。 32ビットビルドでは4、64ビットビルドでは8です。

2

.length

連想配列内の値の数を返します。 動的配列とは異なり、読み取り専用です。

3

.dup

同じサイズの新しい連想配列を作成し、連想配列の内容をそこにコピーします。

4

.keys

要素が連想配列のキーである動的配列を返します。

5

.values

要素が連想配列の値である動的配列を返します。

6

.rehash

ルックアップがより効率的になるように、連想配列を適切に再編成します。 rehashは、たとえば、プログラムがシンボルテーブルのロードを完了し、その中に高速なルックアップを必要とする場合に効果的です。 再編成された配列への参照を返します。

7

.byKey()

連想配列のキーを反復処理するForeachStatementへのAggregateとしての使用に適したデリゲートを返します。

8

.byValue()

連想配列の値を反復処理するForeachStatementへのAggregateとしての使用に適したデリゲートを返します。

9

.get(Key key, lazy Value defVal)

キーを検索します。存在する場合は対応する値を返し、そうでない場合はdefValを評価して返します。

10

.remove(Key key)

キーのオブジェクトを削除します。

上記のプロパティの使用例を以下に示します。

import std.stdio;

void main () {
   int[string] array1;

   array1["test"] = 3;
   array1["test2"] = 20;

   writeln("sizeof: ",array1.sizeof);
   writeln("length: ",array1.length);
   writeln("dup: ",array1.dup);
   array1.rehash;

   writeln("rehashed: ",array1);
   writeln("keys: ",array1.keys);
   writeln("values: ",array1.values);

   foreach (key; array1.byKey) {
      writeln("by key: ",key);
   }

   foreach (value; array1.byValue) {
      writeln("by value ",value);
   }

   writeln("get value for key test: ",array1.get("test",10));
   writeln("get value for key test3: ",array1.get("test3",10));
   array1.remove("test");
   writeln(array1);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

sizeof: 8
length: 2
dup: ["test":3, "test2":20]
rehashed: ["test":3, "test2":20]
keys: ["test", "test2"]
values: [3, 20]
by key: test
by key: test2
by value 3
by value 20
get value for key test: 3
get value for key test3: 10
["test2":20]

Dプログラミング-ポインター

Dプログラミングポインターは簡単で楽しく学ぶことができます。 一部のDプログラミングタスクはポインターを使用してより簡単に実行でき、動的メモリ割り当てなどの他のDプログラミングタスクはそれらなしでは実行できません。 簡単なポインターを以下に示します。

Dのポインター

ポインターは変数を直接指すのではなく、変数のアドレスを指します。 ご存じのように、すべての変数はメモリロケーションであり、すべてのメモリロケーションには、メモリ内のアドレスを示すアンパサンド(&)演算子を使用してアクセスできるアドレスが定義されています。 定義された変数のアドレスを出力する以下を考慮してください-

import std.stdio;

void main () {
   int var1;
   writeln("Address of var1 variable: ",&var1);

   char var2[10];
   writeln("Address of var2 variable: ",&var2);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Address of var1 variable: 7FFF52691928
Address of var2 variable: 7FFF52691930

ポインターとは

  • ポインタ*は、値が別の変数のアドレスである変数です。 他の変数や定数と同様に、操作する前にポインターを宣言する必要があります。 ポインタ変数宣言の一般的な形式は-
type *var-name;

ここで、 type はポインターの基本型です。有効なプログラミングタイプである必要があり、 var-name はポインター変数の名前です。 ポインターの宣言に使用したアスタリスクは、乗算に使用したものと同じアスタリスクです。 しかしながら;このステートメントでは、変数をポインターとして指定するためにアスタリスクが使用されています。 有効なポインター宣言は次のとおりです-

int    *ip;   //pointer to an integer
double *dp;   //pointer to a double
float  *fp;   //pointer to a float
char   *ch    //pointer to character

すべてのポインターの値の実際のデータ型は、整数、浮動小数点、文字、またはその他のいずれであっても同じで、メモリアドレスを表す長い16進数です。 異なるデータ型のポインターの唯一の違いは、ポインターが指す変数または定数のデータ型です。

Dプログラミングでのポインターの使用

ポインターを非常に頻繁に使用する場合、重要な操作はほとんどありません。

  • ポインター変数を定義します
  • 変数のアドレスをポインターに割り当てます
  • 最後に、ポインター変数で使用可能なアドレスの値にアクセスします。

これは、オペランドで指定されたアドレスにある変数の値を返す単項演算子***を使用して行われます。 次の例では、これらの操作を利用しています-

import std.stdio;

void main () {
   int var = 20;  //actual variable declaration.
   int *ip;       //pointer variable
   ip = &var;  //store address of var in pointer variable

   writeln("Value of var variable: ",var);

   writeln("Address stored in ip variable: ",ip);

   writeln("Value of *ip variable: ",*ip);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Value of var variable: 20
Address stored in ip variable: 7FFF5FB7E930
Value of *ip variable: 20

NULLポインタ

割り当てられる正確なアドレスがない場合に備えて、ポインターNULLをポインター変数に割り当てることは常に良い習慣です。 これは、変数宣言時に行われます。 nullが割り当てられたポインターは、 null ポインターと呼ばれます。

nullポインターは、iostreamを含むいくつかの標準ライブラリで定義されたゼロの値を持つ定数です。 次のプログラムを検討してください-

import std.stdio;

void main () {
   int  *ptr = null;
   writeln("The value of ptr is " , ptr) ;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

The value of ptr is null

ほとんどのオペレーティングシステムでは、アドレス0のメモリにアクセスすることは許可されていません。これは、メモリがオペレーティングシステムによって予約されているためです。 しかしながら;メモリアドレス0には特別な意味があります。ポインタがアクセス可能なメモリ位置を指すことを意図していないことを示します。

慣例により、ポインターにヌル(ゼロ)値が含まれている場合、何も指していないと見なされます。 ヌルポインタを確認するには、次のようにif文を使用できます-

if(ptr)    //succeeds if p is not null
if(!ptr)   //succeeds if p is null

したがって、すべての未使用のポインターにnull値が与えられ、nullポインターの使用を避ければ、初期化されていないポインターの誤った誤用を避けることができます。 多くの場合、初期化されていない変数はいくつかのジャンク値を保持し、プログラムのデバッグが困難になります。

ポインタ演算

ポインターで使用できる4つの算術演算子があります:+、-、、および-

ポインター演算を理解するために、アドレス1000を指す ptr という名前の整数ポインターを考えてみましょう。 32ビット整数を想定して、ポインタで次の算術演算を実行してみましょう-

ptr++

次に、ptrがインクリメントされるたびに次の整数を指すため、 ptr は位置1004を指します。 この操作は、メモリ位置の実際の値に影響を与えることなく、ポインタを次のメモリ位置に移動します。

*ptr* がアドレスが1000の文字を指す場合、次の文字は1001で使用できるため、上記の操作は位置1001を指します。

ポインターをインクリメントする

定数ポインターであるため増分できない配列名とは異なり、変数ポインターは増分できるため、プログラムでは配列の代わりにポインターを使用することをお勧めします。 次のプログラムは、配列の後続の各要素にアクセスするために変数ポインタをインクリメントします-

import std.stdio;

const int MAX = 3;

void main () {
   int var[MAX] = [10, 100, 200];
   int *ptr = &var[0];

   for (int i = 0; i < MAX; i++, ptr++) {
      writeln("Address of var[" , i , "] = ",ptr);
      writeln("Value of var[" , i , "] = ",*ptr);
   }
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Address of var[0] = 18FDBC
Value of var[0] = 10
Address of var[1] = 18FDC0
Value of var[1] = 100
Address of var[2] = 18FDC4
Value of var[2] = 200

ポインターと配列

ポインターと配列は強く関連しています。 ただし、ポインターと配列は完全に互換性があるわけではありません。 たとえば、次のプログラムを検討してください-

import std.stdio;

const int MAX = 3;

void main () {
   int var[MAX] = [10, 100, 200];
   int *ptr = &var[0];
   var.ptr[2]  = 290;
   ptr[0] = 220;

   for (int i = 0; i < MAX; i++, ptr++) {
      writeln("Address of var[" , i , "] = ",ptr);
      writeln("Value of var[" , i , "] = ",*ptr);
   }
}

上記のプログラムでは、2番目の要素を設定するためのvar.ptr [2]と、0番目の要素を設定するために使用されるptr [0]を見ることができます。 インクリメント演算子はptrで使用できますが、varでは使用できません。

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Address of var[0] = 18FDBC
Value of var[0] = 220
Address of var[1] = 18FDC0
Value of var[1] = 100
Address of var[2] = 18FDC4
Value of var[2] = 290

ポインターへのポインター

ポインターへのポインターは、複数の間接指定またはポインターのチェーンの形式です。 通常、ポインターには変数のアドレスが含まれます。 ポインターへのポインターを定義すると、最初のポインターには2番目のポインターのアドレスが含まれます。これは、以下に示すように、実際の値を含む場所を指します。

C ++ポインターからポインター

ポインターへのポインターである変数は、そのように宣言する必要があります。 これは、名前の前に追加のアスタリスクを配置することにより行われます。 たとえば、次は、int型のポインタへのポインタを宣言する構文です-

int **var;

ターゲット値がポインタへのポインタによって間接的に指し示されている場合、その値にアクセスするには、次の例に示すように、アスタリスク演算子を2回適用する必要があります-

import std.stdio;

const int MAX = 3;

void main () {
   int var = 3000;
   writeln("Value of var :" , var);

   int *ptr = &var;
   writeln("Value available at *ptr :" ,*ptr);

   int **pptr = &ptr;
   writeln("Value available at **pptr :",**pptr);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Value of var :3000
Value available at *ptr :3000
Value available at **pptr :3000

ポインターを関数に渡す

Dを使用すると、ポインターを関数に渡すことができます。 そのためには、関数パラメーターをポインター型として宣言するだけです。

次の簡単な例では、ポインターを関数に渡します。

import std.stdio;

void main () {
  //an int array with 5 elements.
   int balance[5] = [1000, 2, 3, 17, 50];
   double avg;

   avg = getAverage( &balance[0], 5 ) ;
   writeln("Average is :" , avg);
}

double getAverage(int *arr, int size) {
   int    i;
   double avg, sum = 0;

   for (i = 0; i < size; ++i) {
      sum += arr[i];
   }

   avg = sum/size;
   return avg;
}

上記のコードを一緒にコンパイルして実行すると、次の結果が生成されます-

Average is :214.4

関数からポインターを返す

ポインターを使用して10個の数値を返す次の関数を考えてみましょう。これは、最初の配列要素のアドレスを意味します。

import std.stdio;

void main () {
   int *p = getNumber();

   for ( int i = 0; i < 10; i++ ) {
      writeln("*(p + " , i , ") : ",*(p + i));
   }
}

int * getNumber( ) {
   static int r [10];

   for (int i = 0; i < 10; ++i) {
      r[i] = i;
   }

   return &r[0];
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

*(p + 0) : 0
*(p + 1) : 1
*(p + 2) : 2
*(p + 3) : 3
*(p + 4) : 4
*(p + 5) : 5
*(p + 6) : 6
*(p + 7) : 7
*(p + 8) : 8
*(p + 9) : 9

配列へのポインター

配列名は、配列の最初の要素への定数ポインターです。 したがって、宣言で-

double balance[50];
*balance* は、配列balanceの最初の要素のアドレスである&balance [0]へのポインターです。 したがって、次のプログラムの断片は、 *balance* の最初の要素のアドレスを *p* に割り当てます-
double *p;
double balance[10];

p = balance;

配列名を定数ポインターとして使用すること、およびその逆も有効です。 したがって、*(balance + 4)は、balance [4]でデータにアクセスするための正当な方法です。

最初の要素のアドレスをpに保存すると、* p、(p + 1)、(p + 2)などを使用して配列要素にアクセスできます。 次の例は、上記のすべての概念を示しています-

import std.stdio;

void main () {
  //an array with 5 elements.
   double balance[5] = [1000.0, 2.0, 3.4, 17.0, 50.0];
   double *p;

   p = &balance[0];

  //output each array element's value
   writeln("Array values using pointer " );

   for ( int i = 0; i < 5; i++ ) {
      writeln( "*(p + ", i, ") : ", *(p + i));
   }
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Array values using pointer
*(p + 0) : 1000
*(p + 1) : 2
*(p + 2) : 3.4
*(p + 3) : 17
*(p + 4) : 50

Dプログラミング-タプル

タプルは、複数の値を単一のオブジェクトとして結合するために使用されます。 タプルには一連の要素が含まれます。 要素には、型、式、またはエイリアスを指定できます。 タプルの数と要素はコンパイル時に固定され、実行時に変更することはできません。

タプルには、構造体と配列の両方の特性があります。 タプル要素は、構造体のようなさまざまなタイプにすることができます。 要素は、配列のようなインデックスを介してアクセスできます。 これらは、std.typeconsモジュールのTupleテンプレートによってライブラリ機能として実装されます。 Tupleは、一部の操作にstd.typetupleモジュールのTypeTupleを使用します。

tuple()を使用したタプル

タプルは、関数tuple()によって構築できます。 タプルのメンバーは、インデックス値によってアクセスされます。 例を以下に示します。

import std.stdio;
import std.typecons;

void main() {
   auto myTuple = tuple(1, "Tuts");
   writeln(myTuple);
   writeln(myTuple[0]);
   writeln(myTuple[1]);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Tuple!(int, string)(1, "Tuts")
1
Tuts

タプルテンプレートを使用したタプル

tupleは、tuple()関数の代わりにTupleテンプレートによって直接構築することもできます。 各メンバーのタイプと名前は、2つの連続したテンプレートパラメーターとして指定されます。 テンプレートを使用して作成すると、プロパティによってメンバーにアクセスできます。

import std.stdio;
import std.typecons;

void main() {
   auto myTuple = Tuple!(int, "id",string, "value")(1, "Tuts");
   writeln(myTuple);

   writeln("by index 0 : ", myTuple[0]);
   writeln("by .id : ", myTuple.id);

   writeln("by index 1 : ", myTuple[1]);
   writeln("by .value ", myTuple.value);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます。

Tuple!(int, "id", string, "value")(1, "Tuts")
by index 0 : 1
by .id : 1
by index 1 : Tuts
by .value Tuts

プロパティと関数のパラメーターの拡張

Tupleのメンバーは、.expandプロパティまたはスライスによって展開できます。 この展開/スライスされた値は、関数の引数リストとして渡すことができます。 例を以下に示します。

import std.stdio;
import std.typecons;

void method1(int a, string b, float c, char d) {
   writeln("method 1 ",a,"\t",b,"\t",c,"\t",d);
}

void method2(int a, float b, char c) {
   writeln("method 2 ",a,"\t",b,"\t",c);
}

void main() {
   auto myTuple = tuple(5, "my string", 3.3, 'r');

   writeln("method1 call 1");
   method1(myTuple[]);

   writeln("method1 call 2");
   method1(myTuple.expand);

   writeln("method2 call 1");
   method2(myTuple[0], myTuple[$-2..$]);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

method1 call 1
method 1 5 my string 3.3 r
method1 call 2
method 1 5 my string 3.3 r
method2 call 1
method 2 5 3.3 r

タイプタプル

TypeTupleはstd.typetupleモジュールで定義されています。 値とタイプのコンマ区切りリスト。 TypeTupleを使用した簡単な例を以下に示します。 TypeTupleは、引数リスト、テンプレートリスト、および配列リテラルリストの作成に使用されます。

import std.stdio;
import std.typecons;
import std.typetuple;

alias TypeTuple!(int, long) TL;

void method1(int a, string b, float c, char d) {
   writeln("method 1 ",a,"\t",b,"\t",c,"\t",d);
}

void method2(TL tl) {
   writeln(tl[0],"\t", tl[1] );
}

void main() {
   auto arguments = TypeTuple!(5, "my string", 3.3,'r');
   method1(arguments);
   method2(5, 6L);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

method 1 5 my string 3.3 r
5     6

Dプログラミング-構造体

*structure* は、Dプログラミングで利用できるユーザー定義のデータ型です。これにより、さまざまな種類のデータ項目を組み合わせることができます。

構造は、レコードを表すために使用されます。 図書館で本を追跡したいとします。 あなたは、各本に関する次の属性を追跡することができます-

  • タイトル
  • 著者
  • 件名
  • ブックID

構造の定義

構造を定義するには、 struct ステートメントを使用する必要があります。 structステートメントは、プログラムに複数のメンバーを持つ新しいデータ型を定義します。 構造体ステートメントの形式はこれです-

struct [structure tag] {
   member definition;
   member definition;
   ...
   member definition;
} [one or more structure variables];
  • 構造タグ*はオプションであり、各メンバー定義はint iなどの通常の変数定義です。またはfloat f;または他の有効な変数定義。 セミコロンの前の構造の定義の最後で、オプションの構造変数を1つ以上指定できます。 Books構造を宣言する方法は次のとおりです-
struct Books {
   char [] title;
   char [] author;
   char [] subject;
   int   book_id;
};

構造体メンバーへのアクセス

構造体のメンバーにアクセスするには、* memberアクセス​​演算子(。)を使用します。 メンバーアクセス演算子は、構造変数名とアクセスする構造メンバーの間のピリオドとしてコーディングされます。 *struct キーワードを使用して、構造タイプの変数を定義します。 次の例は、構造の使用法を説明しています-

import std.stdio;

struct Books {
   char [] title;
   char [] author;
   char [] subject;
   int   book_id;
};

void main( ) {
   Books Book1;       /*Declare Book1 of type Book*/
   Books Book2;       /*Declare Book2 of type Book*/

  /*book 1 specification*/
   Book1.title = "D Programming".dup;
   Book1.author = "Raj".dup;
   Book1.subject = "D Programming Tutorial".dup;
   Book1.book_id = 6495407;

  /*book 2 specification*/
   Book2.title = "D Programming".dup;
   Book2.author = "Raj".dup;
   Book2.subject = "D Programming Tutorial".dup;
   Book2.book_id = 6495700;

  /*print Book1 info*/
   writeln( "Book 1 title : ", Book1.title);
   writeln( "Book 1 author : ", Book1.author);
   writeln( "Book 1 subject : ", Book1.subject);
   writeln( "Book 1 book_id : ", Book1.book_id);

  /*print Book2 info*/
   writeln( "Book 2 title : ", Book2.title);
   writeln( "Book 2 author : ", Book2.author);
   writeln( "Book 2 subject : ", Book2.subject);
   writeln( "Book 2 book_id : ", Book2.book_id);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Book 1 title : D Programming
Book 1 author : Raj
Book 1 subject : D Programming Tutorial
Book 1 book_id : 6495407
Book 2 title : D Programming
Book 2 author : Raj
Book 2 subject : D Programming Tutorial
Book 2 book_id : 6495700

関数の引数としての構造

他の変数またはポインターを渡すのと非常によく似た方法で、構造体を関数の引数として渡すことができます。 上記の例でアクセスしたのと同様の方法で構造変数にアクセスします-

import std.stdio;

struct Books {
   char [] title;
   char [] author;
   char [] subject;
   int   book_id;
};

void main( ) {
   Books Book1;       /*Declare Book1 of type Book*/
   Books Book2;       /*Declare Book2 of type Book*/

  /*book 1 specification*/
   Book1.title = "D Programming".dup;
   Book1.author = "Raj".dup;
   Book1.subject = "D Programming Tutorial".dup;
   Book1.book_id = 6495407;

  /*book 2 specification*/
   Book2.title = "D Programming".dup;
   Book2.author = "Raj".dup;
   Book2.subject = "D Programming Tutorial".dup;
   Book2.book_id = 6495700;

  /*print Book1 info*/
   printBook( Book1 );

  /*Print Book2 info*/
   printBook( Book2 );
}

void printBook( Books book ) {
   writeln( "Book title : ", book.title);
   writeln( "Book author : ", book.author);
   writeln( "Book subject : ", book.subject);
   writeln( "Book book_id : ", book.book_id);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Book title : D Programming
Book author : Raj
Book subject : D Programming Tutorial
Book book_id : 6495407
Book title : D Programming
Book author : Raj
Book subject : D Programming Tutorial
Book book_id : 6495700

構造体の初期化

構造体は2つの形式で初期化できます。1つはコンストラクターを使用し、もう1つは\ {}形式を使用します。 例を以下に示します。

import std.stdio;

struct Books {
   char [] title;
   char [] subject = "Empty".dup;
   int   book_id = -1;
   char [] author = "Raj".dup;
};

void main( ) {
   Books Book1 = Books("D Programming".dup, "D Programming Tutorial".dup, 6495407 );
   printBook( Book1 );

   Books Book2 = Books("D Programming".dup,
      "D Programming Tutorial".dup, 6495407,"Raj".dup );
   printBook( Book2 );

   Books Book3 =  {title:"Obj C programming".dup, book_id : 1001};
   printBook( Book3 );
}

void printBook( Books book ) {
   writeln( "Book title : ", book.title);
   writeln( "Book author : ", book.author);
   writeln( "Book subject : ", book.subject);
   writeln( "Book book_id : ", book.book_id);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Book title : D Programming
Book author : Raj
Book subject : D Programming Tutorial
Book book_id : 6495407
Book title : D Programming
Book author : Raj
Book subject : D Programming Tutorial
Book book_id : 6495407
Book title : Obj C programming
Book author : Raj
Book subject : Empty
Book book_id : 1001

静的メンバー

静的変数は一度だけ初期化されます。 たとえば、書籍の一意のIDを取得するには、book_idを静的として作成し、書籍IDをインクリメントします。 例を以下に示します。

import std.stdio;

struct Books {
   char [] title;
   char [] subject = "Empty".dup;
   int   book_id;
   char [] author = "Raj".dup;
   static int id = 1000;
};

void main( ) {
   Books Book1 = Books("D Programming".dup, "D Programming Tutorial".dup,++Books.id );
   printBook( Book1 );

   Books Book2 = Books("D Programming".dup, "D Programming Tutorial".dup,++Books.id);
   printBook( Book2 );

   Books Book3 =  {title:"Obj C programming".dup, book_id:++Books.id};
   printBook( Book3 );
}

void printBook( Books book ) {
   writeln( "Book title : ", book.title);
   writeln( "Book author : ", book.author);
   writeln( "Book subject : ", book.subject);
   writeln( "Book book_id : ", book.book_id);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Book title : D Programming
Book author : Raj
Book subject : D Programming Tutorial
Book book_id : 1001
Book title : D Programming
Book author : Raj
Book subject : D Programming Tutorial
Book book_id : 1002
Book title : Obj C programming
Book author : Raj
Book subject : Empty
Book book_id : 1003

Dプログラミング-連合

*union* は、Dで使用可能な特別なデータ型であり、同じメモリ位置に異なるデータ型を格納できます。 多くのメンバーでユニオンを定義できますが、常に1つのメンバーのみが値を含むことができます。 ユニオンは、同じメモリ位置を複数の目的に使用する効率的な方法を提供します。

Dでの連合の定義

ユニオンを定義するには、構造を定義したときと非常によく似た方法でユニオンステートメントを使用する必要があります。 ユニオンステートメントは、プログラムに複数のメンバーを持つ新しいデータ型を定義します。 ユニオン文の形式は次のとおりです-

union [union tag] {
   member definition;
   member definition;
   ...
   member definition;
} [one or more union variables];
  • unionタグ*はオプションであり、各メンバー定義はint iなどの通常の変数定義です。またはfloat f;または他の有効な変数定義。 ユニオンの定義の最後で、最後のセミコロンの前に、1つ以上のユニオン変数を指定できますが、これはオプションです。 これは、3つのメンバー if 、および str を持つDataという名前の共用体型を定義する方法です-
union Data {
   int i;
   float f;
   char str[20];
} data;
*Data* 型の変数には、整数、浮動小数点数、または文字列を格納できます。 これは、単一の変数(同じメモリ位置)を使用して複数のタイプのデータを保存できることを意味します。 要件に基づいて、ユニオン内で組み込みまたはユーザー定義のデータ型を使用できます。

ユニオンが占有するメモリは、ユニオンの最大メンバーを保持するのに十分な大きさになります。 たとえば、上記の例では、データ型は20バイトのメモリ空間を占有します。これは、これが文字列で占有できる最大の空間だからです。 次の例は、上記のユニオンが占有する合計メモリサイズを表示します-

import std.stdio;

union Data {
   int i;
   float f;
   char str[20];
};

int main( ) {
   Data data;

   writeln( "Memory size occupied by data : ", data.sizeof);

   return 0;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Memory size occupied by data : 20

組合員へのアクセス

組合のメンバーにアクセスするには、* memberアクセス​​演算子(。)*を使用します。 メンバーアクセス演算子は、ユニオン変数名とアクセスするユニオンメンバー間のピリオドとしてコーディングされます。 ユニオンキーワードを使用して、ユニオンタイプの変数を定義します。

次の例は、ユニオンの使用方法を説明しています-

import std.stdio;

union Data {
   int i;
   float f;
   char str[13];
};

void main( ) {
   Data data;

   data.i = 10;
   data.f = 220.5;

   data.str = "D Programming".dup;
   writeln( "size of : ", data.sizeof);
   writeln( "data.i : ", data.i);
   writeln( "data.f : ", data.f);
   writeln( "data.str : ", data.str);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

size of : 16
data.i : 1917853764
data.f : 4.12236e+30
data.str : D Programming

ここでは、変数に割り当てられた最終値がメモリ位置を占有しているため、ユニオンの i および f メンバーの値が破損していることがわかります。これが str メンバーの値が非常にうまく印刷される理由です。 。

今、私たちはユニオンを持つ主な目的である一度に1つの変数を使用する同じ例をもう一度見てみましょう-

変更された例

import std.stdio;

union Data {
   int i;
   float f;
   char str[13];
};
void main( ) {
   Data data;
   writeln( "size of : ", data.sizeof);

   data.i = 10;
   writeln( "data.i : ", data.i);

   data.f = 220.5;
   writeln( "data.f : ", data.f);

   data.str = "D Programming".dup;
   writeln( "data.str : ", data.str);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

size of : 16
data.i : 10
data.f : 220.5
data.str : D Programming

ここでは、一度に1つのメンバーが使用されているため、すべてのメンバーが非常にうまく印刷されています。

Dプログラミング-範囲

範囲は、要素アクセスの抽象化です。 この抽象化により、多数のコンテナタイプで多数のアルゴリズムを使用できます。 範囲は、コンテナの実装方法ではなく、コンテナ要素へのアクセス方法を強調します。 範囲は、型がメンバー関数の特定のセットを定義するかどうかに基づく非常に単純な概念です。

範囲はDの不可欠な部分です。 Dのスライスは偶然、最も強力な範囲であるRandomAccessRangeの実装であり、Phobosには多くの範囲機能があります。 多くのPhobosアルゴリズムは一時的な範囲オブジェクトを返します。 たとえば、filter()は、次のコードで10より大きい要素を選択し、実際には配列ではなく範囲オブジェクトを返します。

番号範囲

番号範囲は非常に一般的に使用され、これらの番号範囲はint型です。 番号範囲のいくつかの例を以下に示します-

//Example 1
foreach (value; 3..7)

//Example 2
int[] slice = array[5..10];

フォボス山脈

構造体とクラスインターフェイスに関連する範囲は、phobosの範囲です。 Phobosは、D言語コンパイラに付属する公式のランタイムおよび標準ライブラリです。

を含む範囲のさまざまなタイプがあります-

  • 入力範囲
  • ForwardRange
  • BidirectionalRange
  • RandomAccessRange
  • 出力範囲

入力範囲

最も単純な範囲は入力範囲です。 他の範囲は、それらが基づく範囲に加えてより多くの要件をもたらします。 InputRangeが必要とする3つの機能があります-

  • -範囲が空かどうかを指定します。範囲が空と見なされる場合、trueを返す必要があります。そうでない場合はfalse。
  • front -範囲の先頭にある要素へのアクセスを提供します。
  • * popFront()*-最初の要素を削除することにより、最初から範囲を短くします。

import std.stdio;
import std.string;

struct Student {
   string name;
   int number;

   string toString() const {
      return format("%s(%s)", name, number);
   }
}

struct School {
   Student[] students;
}
struct StudentRange {
   Student[] students;

   this(School school) {
      this.students = school.students;
   }
   @property bool empty() const {
      return students.length == 0;
   }
   @property ref Student front() {
      return students[0];
   }
   void popFront() {
      students = students[1 .. $];
   }
}

void main() {
   auto school = School([ Student("Raj", 1), Student("John", 2), Student("Ram", 3)]);
   auto range = StudentRange(school);
   writeln(range);

   writeln(school.students.length);

   writeln(range.front);

   range.popFront;

   writeln(range.empty);
   writeln(range);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

[Raj(1), John(2), Ram(3)]
3
Raj(1)
false
[John(2), Ram(3)]

ForwardRange

ForwardRangeはさらに、InputRangeの他の3つの関数からのsaveメンバー関数部分を必要とし、save関数が呼び出されたときに範囲のコピーを返します。

import std.array;
import std.stdio;
import std.string;
import std.range;

struct FibonacciSeries {
   int first = 0;
   int second = 1;
   enum empty = false;  // infinite range

   @property int front() const {
      return first;
   }
   void popFront() {
      int third = first + second;
      first = second;
      second = third;
   }
   @property FibonacciSeries save() const {
      return this;
   }
}

void report(T)(const dchar[] title, const ref T range) {
   writefln("%s: %s", title, range.take(5));
}

void main() {
   auto range = FibonacciSeries();
   report("Original range", range);

   range.popFrontN(2);
   report("After removing two elements", range);

   auto theCopy = range.save;
   report("The copy", theCopy);

   range.popFrontN(3);
   report("After removing three more elements", range);
   report("The copy", theCopy);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Original range: [0, 1, 1, 2, 3]
After removing two elements: [1, 2, 3, 5, 8]
The copy: [1, 2, 3, 5, 8]
After removing three more elements: [5, 8, 13, 21, 34]
The copy: [1, 2, 3, 5, 8]

BidirectionalRange

BidirectionalRangeは、ForwardRangeのメンバー関数に対して2つのメンバー関数を追加で提供します。 frontと同様のback関数は、範囲の最後の要素へのアクセスを提供します。 popBack関数はpopFront関数に似ており、範囲から最後の要素を削除します。

import std.array;
import std.stdio;
import std.string;

struct Reversed {
   int[] range;

   this(int[] range) {
      this.range = range;
   }
   @property bool empty() const {
      return range.empty;
   }
   @property int front() const {
      return range.back; // reverse
   }
   @property int back() const {
      return range.front;//reverse
   }
   void popFront() {
      range.popBack();
   }
   void popBack() {
      range.popFront();
   }
}

void main() {
   writeln(Reversed([ 1, 2, 3]));
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

[3, 2, 1]

無限のRandomAccessRange

ForwardRangeと比較すると、opIndex()がさらに必要です。 また、空の関数の値は、コンパイル時にfalseとして認識されます。 簡単な例は、正方形の範囲で説明されています。

import std.array;
import std.stdio;
import std.string;
import std.range;
import std.algorithm;

class SquaresRange {
   int first;
   this(int first = 0) {
      this.first = first;
   }
   enum empty = false;
   @property int front() const {
      return opIndex(0);
   }
   void popFront() {
      ++first;
   }
   @property SquaresRange save() const {
      return new SquaresRange(first);
   }
   int opIndex(size_t index) const {
     /*This function operates at constant time*/
      immutable integerValue = first + cast(int)index;
      return integerValue * integerValue;
   }
}

bool are_lastTwoDigitsSame(int value) {
  /*Must have at least two digits*/
   if (value < 10) {
      return false;
   }

  /*Last two digits must be divisible by 11*/
   immutable lastTwoDigits = value % 100;
   return (lastTwoDigits % 11) == 0;
}

void main() {
   auto squares = new SquaresRange();

   writeln(squares[5]);

   writeln(squares[10]);

   squares.popFrontN(5);
   writeln(squares[0]);

   writeln(squares.take(50).filter!are_lastTwoDigitsSame);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

25
100
25
[100, 144, 400, 900, 1444, 1600, 2500]

有限RandomAccessRange

双方向範囲と比較した場合、opIndex()とlengthがさらに必要です。 これは、前に使用したフィボナッチ数列と二乗範囲の例を使用した詳細な例を使用して説明します。 この例は、通常のDコンパイラではうまく機能しますが、オンラインコンパイラでは機能しません。

import std.array;
import std.stdio;
import std.string;
import std.range;
import std.algorithm;

struct FibonacciSeries {
   int first = 0;
   int second = 1;
   enum empty = false;  // infinite range

   @property int front() const {
      return first;
   }
   void popFront() {
      int third = first + second;
      first = second;
      second = third;
   }
   @property FibonacciSeries save() const {
      return this;
   }
}

void report(T)(const dchar[] title, const ref T range) {
   writefln("%40s: %s", title, range.take(5));
}

class SquaresRange {
   int first;
   this(int first = 0) {
      this.first = first;
   }
   enum empty = false;
   @property int front() const {
      return opIndex(0);
   }
   void popFront() {
      ++first;
   }
   @property SquaresRange save() const {
      return new SquaresRange(first);
   }
   int opIndex(size_t index) const {
     /*This function operates at constant time*/
      immutable integerValue = first + cast(int)index;
      return integerValue * integerValue;
   }
}

bool are_lastTwoDigitsSame(int value) {
  /*Must have at least two digits*/
   if (value < 10) {
      return false;
   }

  /*Last two digits must be divisible by 11*/
   immutable lastTwoDigits = value % 100;
   return (lastTwoDigits % 11) == 0;
}

struct Together {
   const(int)[][] slices;
   this(const(int)[][] slices ...) {
      this.slices = slices.dup;
      clearFront();
      clearBack();
   }
   private void clearFront() {
      while (!slices.empty && slices.front.empty) {
         slices.popFront();
      }
   }
   private void clearBack() {
      while (!slices.empty && slices.back.empty) {
         slices.popBack();
      }
   }
   @property bool empty() const {
      return slices.empty;
   }
   @property int front() const {
      return slices.front.front;
   }
   void popFront() {
      slices.front.popFront();
      clearFront();
   }
   @property Together save() const {
      return Together(slices.dup);
   }
   @property int back() const {
      return slices.back.back;
   }
   void popBack() {
      slices.back.popBack();
      clearBack();
   }
   @property size_t length() const {
      return reduce!((a, b) => a + b.length)(size_t.init, slices);
   }
   int opIndex(size_t index) const {
     /*Save the index for the error message*/
      immutable originalIndex = index;

      foreach (slice; slices) {
         if (slice.length > index) {
            return slice[index];
         } else {
            index -= slice.length;
         }
      }
      throw new Exception(
         format("Invalid index: %s (length: %s)", originalIndex, this.length));
   }
}
void main() {
   auto range = Together(FibonacciSeries().take(10).array, [ 777, 888 ],
      (new SquaresRange()).take(5).array);
   writeln(range.save);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 777, 888, 0, 1, 4, 9, 16]

出力範囲

OutputRangeは、文字をstdoutに送信するのと同様に、ストリーミングされた要素の出力を表します。 OutputRangeでは、put(range、element)操作のサポートが必要です。 put()は、std.rangeモジュールで定義されている関数です。 コンパイル時に範囲と要素の機能を決定し、要素の出力に使用する最も適切な方法を使用します。 以下に簡単な例を示します。

import std.algorithm;
import std.stdio;

struct MultiFile {
   string delimiter;
   File[] files;

   this(string delimiter, string[] fileNames ...) {
      this.delimiter = delimiter;

     /*stdout is always included*/
      this.files ~= stdout;

     /*A File object for each file name*/
      foreach (fileName; fileNames) {
         this.files ~= File(fileName, "w");
      }
   }
   void put(T)(T element) {
      foreach (file; files) {
         file.write(element, delimiter);
      }
   }
}
void main() {
   auto output = MultiFile("\n", "output_0", "output_1");
   copy([ 1, 2, 3], output);
   copy([ "red", "blue", "green" ], output);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

[1, 2, 3]
["red", "blue", "green"]

Dプログラミング-エイリアス

エイリアスは、名前が指すように、既存の名前の代替名を提供します。 エイリアスの構文は次のとおりです。

alias new_name = existing_name;

古い形式の例を参照する場合に備えて、次は古い構文です。 これの使用は強く推奨されません。

alias existing_name new_name;

また、式で使用される別の構文があり、式の代わりにエイリアス名を直接使用できる下に示します。

alias expression alias_name ;

ご存知かもしれませんが、typedefは新しい型を作成する機能を追加します。 エイリアスは、typedefなどの機能を実行できます。 型変換機能を提供するstd.convヘッダーを使用するエイリアスの簡単な使用例を次に示します。

import std.stdio;
import std.conv:to;

alias to!(string) toString;

void main() {
   int a = 10;
   string s = "Test"~toString(a);
   writeln(s);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Test10

上記の例では、to!string(a)を使用する代わりに、エイリアス名toStringに割り当てて、より便利で簡単に理解できるようにしました。

タプルのエイリアス

タプルのエイリアス名を設定できる別の例を見てみましょう。

import std.stdio;
import std.typetuple;

alias TypeTuple!(int, long) TL;

void method1(TL tl) {
   writeln(tl[0],"\t", tl[1] );
}

void main() {
   method1(5, 6L);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

5   6

上記の例では、タイプタプルがエイリアス変数に割り当てられ、メソッド定義と変数のアクセスを簡素化します。 この種のアクセスは、そのようなタイプのタプルを再利用しようとする場合にさらに便利です。

データ型のエイリアス

多くの場合、アプリケーション全体で使用する必要がある一般的なデータ型を定義します。 複数のプログラマーがアプリケーションをコーディングする場合、1人がint、もう1人がdoubleなどを使用する場合があります。 このような競合を避けるために、データ型に型を使用することがよくあります。 以下に簡単な例を示します。

import std.stdio;

alias int myAppNumber;
alias string myAppString;

void main() {
   myAppNumber i = 10;
   myAppString s = "TestString";

   writeln(i,s);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

10TestString

クラス変数のエイリアス

サブクラス内のスーパークラスのメンバー変数にアクセスする必要があることがよくあります。これは、別名を使用して、別名で可能にすることができます。

クラスと継承の概念に慣れていない場合は、このセクションを始める前にlink:/d_programming/d_programming_classes_objects [classes]およびlink:/d_programming/d_programming_inheritance [inheritance]のチュートリアルをご覧ください。

以下に簡単な例を示します。

import std.stdio;

class Shape {
   int area;
}

class Square : Shape {
   string name() const @property {
      return "Square";
   }
   alias Shape.area squareArea;
}

void main() {
   auto square = new Square;
   square.squareArea = 42;
   writeln(square.name);
   writeln(square.squareArea);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Square
42

エイリアスこれ

これにより、ユーザー定義型の自動型変換機能が提供されます。 以下に構文を示します。キーワードaliasとこれは、メンバー変数またはメンバー関数の両側に記述されています。

alias member_variable_or_member_function this;

以下にエイリアスthisの威力を示す例を示します。

import std.stdio;

struct Rectangle {
   long length;
   long breadth;

   double value() const @property {
      return cast(double) length *breadth;
   }
   alias value this;
}
double volume(double rectangle, double height) {
   return rectangle* height;
}

void main() {
   auto rectangle = Rectangle(2, 3);
   writeln(volume(rectangle, 5));
}

上記の例では、このメソッドのエイリアスを使用して、構造体の四角形がdouble値に変換されることがわかります。

上記のコードをコンパイルして実行すると、次の結果が生成されます-

30

Dプログラミング-Mixins

ミックスインは、生成されたコードをソースコードに混合できる構造体です。 ミックスインは次のタイプにすることができます-

  • ストリングミックスイン
  • テンプレートミックスイン
  • Mixin名前空間

ストリングミックスイン

Dは、文字列がコンパイル時に既知である限り、文字列としてコードを挿入する機能を備えています。 文字列ミックスインの構文は以下に示されています-

mixin (compile_time_generated_string)

文字列ミックスインの簡単な例を以下に示します。

import std.stdio;

void main() {
   mixin(`writeln("Hello World!");`);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Hello World!

mixinが関数を使用してコードを再利用できるように、コンパイル時に文字列を渡すことができる別の例を次に示します。 以下に示します。

import std.stdio;

string print(string s) {
   return `writeln("` ~ s ~ `");`;
}

void main() {
   mixin (print("str1"));
   mixin (print("str2"));
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

str1
str2

テンプレートミックスイン

Dテンプレートは、コンパイラがそのパターンから実際のインスタンスを生成するための一般的なコードパターンを定義します。 テンプレートは、関数、構造体、共用体、クラス、インターフェース、およびその他の正当なDコードを生成できます。 テンプレートミックスインの構文は次のとおりです。

mixin a_template!(template_parameters)

文字列ミックスインの簡単な例を以下に示します。ここでは、クラスDepartmentを持つテンプレートを作成し、テンプレートをインスタンス化するミックスインを作成して、構造体カレッジで関数setNameおよびprintNamesを使用できるようにします。

import std.stdio;

template Department(T, size_t count) {
   T[count] names;
   void setName(size_t index, T name) {
      names[index] = name;
   }

   void printNames() {
      writeln("The names");

      foreach (i, name; names) {
         writeln(i," : ", name);
      }
   }
}

struct College {
   mixin Department!(string, 2);
}

void main() {
   auto college = College();
   college.setName(0, "name1");
   college.setName(1, "name2");
   college.printNames();
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

The names
0 : name1
1 : name2

Mixin名前空間

ミックスインの名前空間は、テンプレートミックスインのあいまいさを避けるために使用されます。 たとえば、2つの変数があり、1つはmainで明示的に定義され、もう1つは混在しています。 混在する名前が周囲のスコープ内の名前と同じ場合、周囲のスコープ内の名前が使用されます。 この例を以下に示します。

import std.stdio;

template Person() {
   string name;

   void print() {
      writeln(name);
   }
}

void main() {
   string name;

   mixin Person a;
   name = "name 1";
   writeln(name);

   a.name = "name 2";
   print();
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

name 1
name 2

Dプログラミング-モジュール

モジュールはDの構成要素です。 それらは単純な概念に基づいています。 すべてのソースファイルはモジュールです。 したがって、プログラムを記述する単一のファイルは個々のモジュールです。 デフォルトでは、モジュールの名前は拡張子.dを除いたファイル名と同じです。

明示的に指定された場合、モジュールの名前はmoduleキーワードで定義され、ソースファイルの最初の非コメント行として表示される必要があります。 たとえば、ソースファイルの名前が「employee.d」であるとします。 次に、モジュールの名前は、_module_キーワードに続いて_employee_で指定されます。 以下のとおりです。

module employee;

class Employee {
  //Class definition goes here.
}

モジュール行はオプションです。 指定しない場合、.d拡張子のないファイル名と同じです。

ファイル名とモジュール名

Dは、ソースコードとモジュール名でUnicodeをサポートします。 ただし、ファイルシステムのUnicodeサポートは異なります。 たとえば、ほとんどのLinuxファイルシステムはUnicodeをサポートしていますが、Windowsファイルシステムのファイル名では小文字と大文字が区別されない場合があります。 さらに、ほとんどのファイルシステムでは、ファイル名およびディレクトリ名に使用できる文字が制限されています。 移植性の理由から、ファイル名には小文字のASCII文字のみを使用することをお勧めします。 たとえば、「employee.d」は、従業員という名前のクラスに適したファイル名です。

したがって、モジュールの名前もASCII文字で構成されます-

module employee; //Module name consisting of ASCII letters

class eëmployëë { }

Dパッケージ

関連モジュールの組み合わせはパッケージと呼ばれます。 Dパッケージも単純な概念です。同じディレクトリ内にあるソースファイルは、同じパッケージに属すると見なされます。 ディレクトリの名前はパッケージの名前になります。パッケージの名前は、モジュール名の最初の部分としても指定する必要があります。

たとえば、「employee.d」と「office.d」がディレクトリ「company」内にある場合、モジュール名とともにディレクトリ名を指定すると、それらは同じパッケージの一部になります-

module company.employee;

class Employee { }

同様に、オフィスモジュールの場合-

module company.office;

class Office { }

パッケージ名はディレクトリ名に対応するため、1つのディレクトリレベルよりも深いモジュールのパッケージ名は、その階層を反映する必要があります。 たとえば、「company」ディレクトリに「branch」ディレクトリが含まれている場合、そのディレクトリ内のモジュールの名前にもbranchが含まれます。

module company.branch.employee;

プログラムでのモジュールの使用

これまでほとんどすべてのプログラムで使用してきたimportキーワードは、現在のモジュールにモジュールを導入するためのものです-

import std.stdio;

モジュール名にはパッケージ名も含まれる場合があります。 たとえば、std。 上記の部分は、stdioがstdパッケージの一部であるモジュールであることを示しています。

モジュールの場所

コンパイラは、パッケージ名とモジュール名をディレクトリ名とファイル名に直接変換することにより、モジュールファイルを見つけます。

たとえば、従業員とオフィスの2つのモジュールは、それぞれ「company/employee.d」と「animal/office.d」(または、「company \ employee.d」と「company \ office.d」に応じて、 company.employeeおよびcompany.officeのファイルシステム)。

長いモジュール名と短いモジュール名

プログラムで使用される名前は、以下に示すようにモジュール名とパッケージ名で綴られている場合があります。

import company.employee;
auto employee0 = Employee();
auto employee1 = company.employee.Employee();

通常、長い名前は必要ありませんが、名前の競合が発生する場合があります。 たとえば、複数のモジュールに表示される名前を参照する場合、コンパイラはどちらを意味するかを決定できません。 次のプログラムは、2つの個別のモジュールで定義されている2つの個別の_employee_構造体を区別するために、長い名前を綴っています:company_および_college.

フォルダcompanyの最初の従業員モジュールは次のとおりです。

module company.employee;

import std.stdio;

class Employee {
   public:
      string str;

   void print() {
      writeln("Company Employee: ",str);
   }
}

フォルダカレッジの2番目の従業員モジュールは次のとおりです。

module college.employee;

import std.stdio;

class Employee {
   public:
      string str;

   void print() {
      writeln("College Employee: ",str);
   }
}

hello.dのメインモジュールは、collegeおよびcompanyフォルダーを含むフォルダーに保存する必要があります。 以下の通りです。

import company.employee;
import college.employee;

import std.stdio;

void main() {
   auto myemployee1 = new company.employee.Employee();
   myemployee1.str = "emp1";
   myemployee1.print();

   auto myemployee2 = new college.employee.Employee();
   myemployee2.str = "emp2";
   myemployee2.print();
}

importキーワードは、モジュールをプログラムの一部にするには不十分です。 現在のモジュール内のモジュールの機能を利用できるようにします。 それだけがコードをコンパイルするために必要です。

上記のプログラムをビルドするには、「company/employee.d」と「college/employee.d」もコンパイル行で指定する必要があります。

上記のコードをコンパイルして実行すると、次の結果が生成されます-

$ dmd hello.d company/employee.d college/employee.d -ofhello.amx
$ ./hello.amx
Company Employee: emp1
College Employee: emp2

Dプログラミング-テンプレート

テンプレートは汎用プログラミングの基盤であり、特定のタイプに依存しない方法でコードを記述する必要があります。

テンプレートは、汎用クラスまたは関数を作成するための青写真または式です。

テンプレートは、コンパイラがプログラムコードを自動的に生成するために、コードをパターンとして記述することを可能にする機能です。 ソースコードの一部は、その部分が実際にプログラムで使用されるまで入力されるためにコンパイラーに委ねられます。 コンパイラは不足している部分を埋めます。

機能テンプレート

関数をテンプレートとして定義すると、使用する1つ以上の型が未指定のままになり、後でコンパイラーによって推測されます。 指定されていないタイプは、テンプレートパラメータリスト内で定義されます。テンプレートパラメータリストは、関数の名前と関数パラメータリストの間にあります。 そのため、関数テンプレートには2つのパラメーターリストがあります-

  • テンプレートパラメータリスト
  • 関数パラメータリスト
import std.stdio;

void print(T)(T value) {
   writefln("%s", value);
}

void main() {
   print(42);

   print(1.2);

   print("test");
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

42
1.2
test

複数の型パラメーターを持つ関数テンプレート

複数のパラメータータイプがあります。 それらを次の例に示します。

import std.stdio;

void print(T1, T2)(T1 value1, T2 value2) {
   writefln(" %s %s", value1, value2);
}

void main() {
   print(42, "Test");

   print(1.2, 33);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

 42 Test
 1.2 33

クラステンプレート

関数テンプレートを定義できるように、クラステンプレートも定義できます。 次の例では、Stackクラスを定義し、スタックから要素をプッシュおよびポップする汎用メソッドを実装しています。

import std.stdio;
import std.string;

class Stack(T) {
   private:
      T[] elements;
   public:
      void push(T element) {
         elements ~= element;
      }
      void pop() {
         --elements.length;
      }
      T top() const @property {
         return elements[$ - 1];
      }
      size_t length() const @property {
         return elements.length;
      }
}

void main() {
   auto stack = new Stack!string;

   stack.push("Test1");
   stack.push("Test2");

   writeln(stack.top);
   writeln(stack.length);

   stack.pop;
   writeln(stack.top);
   writeln(stack.length);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Test2
2
Test1
1

Dプログラミング-不変

多くの場合、可変の変数を使用しますが、多くの場合、可変性は必要ありません。 そのような場合、不変変数を使用できます。 不変変数を使用できるいくつかの例を以下に示します。

  • 変化しない_pi_などの数学定数の場合。
  • 値を保持したい配列の場合で、それは突然変異の要件ではありません。

不変性は、変数が不変か可変かを理解することを可能にし、特定の操作が特定の変数を変更しないことを保証します。 また、特定の種類のプログラムエラーのリスクも低減します。 Dの不変性の概念は、constおよびimmutableキーワードで表されます。 2つの単語自体の意味は似ていますが、プログラムでの責任は異なり、互換性がない場合もあります。

Dの不変性の概念は、constおよびimmutableキーワードで表されます。 2つの単語自体の意味は似ていますが、プログラムでの責任は異なり、互換性がない場合もあります。

Dの不変変数のタイプ

絶対に変更できない3つのタイプの定義変数があります。

  • 列挙定数
  • 不変変数
  • const変数

enum Dの定数

enum定数により、定数値を意味のある名前に関連付けることができます。 以下に簡単な例を示します。

import std.stdio;

enum Day{
   Sunday = 1,
   Monday,
   Tuesday,
   Wednesday,
   Thursday,
   Friday,
   Saturday
}

void main() {
   Day day;
   day = Day.Sunday;

   if (day == Day.Sunday) {
      writeln("The day is Sunday");
   }
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

The day is Sunday

Dの不変変数

不変変数は、プログラムの実行中に決定できます。 初期化後に不変になるようコンパイラーに指示します。 以下に簡単な例を示します。

import std.stdio;
import std.random;

void main() {
   int min = 1;
   int max = 10;

   immutable number = uniform(min, max + 1);
  //cannot modify immutable expression number
  //number = 34;
   typeof(number) value = 100;

   writeln(typeof(number).stringof, number);
   writeln(typeof(value).stringof, value);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

immutable(int)4
immutable(int)100

上記の例では、データ型を別の変数に転送し、印刷中にstringofを使用する方法がわかります。

Dの_Const_変数

定数変数は、不変と同様に変更できません。 不変変数は、不変パラメーターとして関数に渡すことができるため、constでは不変を使用することをお勧めします。 前に使用したのと同じ例が、以下に示すようにconstに変更されます。

import std.stdio;
import std.random;

void main() {
   int min = 1;
   int max = 10;

   const number = uniform(min, max + 1);
  //cannot modify const expression number|
  //number = 34;
   typeof(number) value = 100;

   writeln(typeof(number).stringof, number);
   writeln(typeof(value).stringof, value);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

const(int)7
const(int)100

Dの不変パラメーター

constは、元の変数が可変であるか不変であるかに関する情報を消去するため、不変を使用すると、元の型を保持したまま他の関数に渡されます。 以下に簡単な例を示します。

import std.stdio;

void print(immutable int[] array) {
   foreach (i, element; array) {
      writefln("%s: %s", i, element);
   }
}

void main() {
   immutable int[] array = [ 1, 2 ];
   print(array);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

0: 1
1: 2

Dプログラミング-ファイルI/O

ファイルは、std.stdioモジュールの_File_構造体で表されます。 ファイルは一連のバイトを表し、テキストファイルまたはバイナリファイルのどちらでもかまいません。

Dプログラミング言語は、ストレージデバイス上のファイルを処理するための低レベル(OSレベル)呼び出しだけでなく、高レベル関数へのアクセスを提供します。

Dでファイルを開く

標準の入力および出力ストリームstdinおよびstdoutは、プログラムの実行開始時にすでに開いています。 すぐに使用できます。 一方、ファイルの名前と必要なアクセス権を指定して、最初にファイルを開く必要があります。

File file = File(filepath, "mode");

ここで、*ファイル名*は文字列リテラルであり、ファイルに名前を付けてアクセスするために使用し、*モード*は次のいずれかの値を持つことができます-

Sr.No. Mode & Description
1

r

読み取り用に既存のテキストファイルを開きます。

2

w

書き込み用にテキストファイルを開きます。テキストファイルが存在しない場合は、新しいファイルが作成されます。 ここで、プログラムはファイルの先頭からコンテンツの書き込みを開始します。

3

a

追加モードで書き込むためにテキストファイルを開きます。存在しない場合は、新しいファイルが作成されます。 ここで、プログラムは既存のファイルコンテンツにコンテンツを追加し始めます。

4

r+

読み取りと書き込みの両方のためにテキストファイルを開きます。

5

w+

読み取りと書き込みの両方のためにテキストファイルを開きます。 最初にファイルが存在する場合はファイルをゼロ長に切り捨て、存在しない場合はファイルを作成します。

6

a+

読み取りと書き込みの両方のためにテキストファイルを開きます。 ファイルが存在しない場合は作成します。 読み取りは最初から開始されますが、書き込みは追加のみ可能です。

Dでファイルを閉じる

ファイルを閉じるには、fileがファイル参照を保持しているfile.close()関数を使用します。 この関数のプロトタイプは-

file.close();

プログラムによって開かれたファイルは、プログラムがそのファイルの使用を終了したときに閉じる必要があります。 ほとんどの場合、ファイルを明示的に閉じる必要はありません。 Fileオブジェクトが終了すると、自動的に閉じられます。

Dでファイルを書く

file.writelnは、開いているファイルに書き込むために使用されます。

file.writeln("hello");
import std.stdio;
import std.file;

void main() {
   File file = File("test.txt", "w");
   file.writeln("hello");
   file.close();
}

上記のコードがコンパイルされて実行されると、新しいファイル test.txt が、それが起動されたディレクトリ(プログラムの作業ディレクトリ内)に作成されます。

Dでのファイルの読み取り

次の方法は、ファイルから単一の行を読み取ります-

string s = file.readln();

読み取りと書き込みの完全な例を以下に示します。

import std.stdio;
import std.file;

void main() {
   File file = File("test.txt", "w");
   file.writeln("hello");
   file.close();
   file = File("test.txt", "r");

   string s = file.readln();
   writeln(s);

   file.close();
}

上記のコードがコンパイルおよび実行されると、前のセクションで作成されたファイルを読み取り、次の結果を生成します-

hello

ファイルの終わりまでファイルを読み込む別の例を次に示します。

import std.stdio;
import std.string;

void main() {
   File file = File("test.txt", "w");
   file.writeln("hello");
   file.writeln("world");
   file.close();
   file = File("test.txt", "r");

   while (!file.eof()) {
      string line = chomp(file.readln());
      writeln("line -", line);
   }
}

上記のコードがコンパイルおよび実行されると、前のセクションで作成されたファイルを読み取り、次の結果を生成します-

line -hello
line -world
line -

上記の例では、writelnが実行されるとwritelnが次の行に移動するため、空の3行目が表示されます。

Dプログラミング-並行性

並行性とは、プログラムを一度に複数のスレッドで実行することです。 並行プログラムの例は、多数のクライアントに同時に応答するWebサーバーです。 並行性はメッセージの受け渡しでは簡単ですが、データ共有に基づいている場合は非常に記述が困難です。

スレッド間で渡されるデータはメッセージと呼ばれます。 メッセージは、任意のタイプと任意の数の変数で構成できます。 すべてのスレッドにはIDがあり、メッセージの受信者を指定するために使用されます。 別のスレッドを開始するスレッドは、新しいスレッドの所有者と呼ばれます。

Dでのスレッドの開始

関数spawn()は、パラメーターとしてポインターを取り、その関数から新しいスレッドを開始します。 呼び出される可能性のある他の関数を含む、その関数によって実行される操作はすべて、新しいスレッドで実行されます。 所有者とワーカーの両方が、独立したプログラムであるかのように別々に実行を開始します。

import std.stdio;
import std.stdio;
import std.concurrency;
import core.thread;

void worker(int a) {
   foreach (i; 0 .. 4) {
      Thread.sleep(1);
      writeln("Worker Thread ",a + i);
   }
}

void main() {
   foreach (i; 1 .. 4) {
      Thread.sleep(2);
      writeln("Main Thread ",i);
      spawn(&ap;worker, i *5);
   }

   writeln("main is done.");
}

上記のコードがコンパイルおよび実行されると、前のセクションで作成されたファイルを読み取り、次の結果を生成します-

Main Thread 1
Worker Thread 5
Main Thread 2
Worker Thread 6
Worker Thread 10
Main Thread 3
main is done.
Worker Thread 7
Worker Thread 11
Worker Thread 15
Worker Thread 8
Worker Thread 12
Worker Thread 16
Worker Thread 13
Worker Thread 17
Worker Thread 18

Dのスレッド識別子

モジュールレベルでグローバルに使用できる_thisTid_変数は、常に現在のスレッドのIDです。 また、spawnが呼び出されたときにthreadIdを受け取ることができます。 例を以下に示します。

import std.stdio;
import std.concurrency;

void printTid(string tag) {
   writefln("%s: %s, address: %s", tag, thisTid, &thisTid);
}

void worker() {
   printTid("Worker");
}

void main() {
   Tid myWorker = spawn(&worker);

   printTid("Owner ");

   writeln(myWorker);
}

上記のコードがコンパイルおよび実行されると、前のセクションで作成されたファイルを読み取り、次の結果を生成します-

Owner : Tid(std.concurrency.MessageBox), address: 10C71A59C
Worker: Tid(std.concurrency.MessageBox), address: 10C71A59C
Tid(std.concurrency.MessageBox)

Dでメッセージを渡す

関数send()はメッセージを送信し、関数receiveOnly()は特定のタイプのメッセージを待ちます。 prioritySend()、receive()、およびreceiveTimeout()という名前の他の関数があり、これらについては後で説明します。

次のプログラムの所有者は、ワーカーにint型のメッセージを送信し、double型のワーカーからのメッセージを待ちます。 所有者が負のintを送信するまで、スレッドはメッセージを繰り返し送信します。 例を以下に示します。

import std.stdio;
import std.concurrency;
import core.thread;
import std.conv;

void workerFunc(Tid tid) {
   int value = 0;
   while (value >= 0) {
      value = receiveOnly!int();
      auto result = to!double(value)* 5; tid.send(result);
   }
}

void main() {
   Tid worker = spawn(&workerFunc,thisTid);

   foreach (value; 5 .. 10) {
      worker.send(value);
      auto result = receiveOnly!double();
      writefln("sent: %s, received: %s", value, result);
   }

   worker.send(-1);
}

上記のコードがコンパイルおよび実行されると、前のセクションで作成されたファイルを読み取り、次の結果を生成します-

sent: 5, received: 25
sent: 6, received: 30
sent: 7, received: 35
sent: 8, received: 40
sent: 9, received: 45

Dで待機するメッセージの受け渡し

待機でメッセージを渡す簡単な例を以下に示します。

import std.stdio;
import std.concurrency;
import core.thread;
import std.conv;

void workerFunc(Tid tid) {
   Thread.sleep(dur!("msecs")( 500 ),);
   tid.send("hello");
}

void main() {
   spawn(&workerFunc,thisTid);
   writeln("Waiting for a message");
   bool received = false;

   while (!received) {
      received = receiveTimeout(dur!("msecs")( 100 ), (string message) {
         writeln("received: ", message);
      });

      if (!received) {
         writeln("... no message yet");
      }
   }
}

上記のコードがコンパイルおよび実行されると、前のセクションで作成されたファイルを読み取り、次の結果を生成します-

Waiting for a message
... no message yet
... no message yet
... no message yet
... no message yet
received: hello

Dプログラミング-例外処理

例外は、プログラムの実行中に発生する問題です。 D例外は、ゼロ除算の試行など、プログラムの実行中に発生する例外的な状況への応答です。

例外は、プログラムのある部分から別の部分に制御を移す方法を提供します。 D例外処理は、3つのキーワード trycatch 、および throw に基づいています。

  • throw -問題が発生すると、プログラムは例外をスローします。 これは、 throw キーワードを使用して行われます。
  • catch -プログラムは、問題を処理するプログラム内の場所で例外ハンドラーを使用して例外をキャッチします。 catch キーワードは、例外のキャッチを示します。
  • try - try ブロックは、特定の例外がアクティブになるコードのブロックを識別します。 その後に1つ以上のcatchブロックが続きます。

ブロックが例外を発生させると仮定すると、メソッドは try キーワードと catch キーワードの組み合わせを使用して例外をキャッチします。 try/catchブロックは、例外を生成する可能性のあるコードの周りに配置されます。 try/catchブロック内のコードは保護されたコードと呼ばれ、try/catchを使用するための構文は次のようになります-

try {
  //protected code
}
catch( ExceptionName e1 ) {
  //catch block
}
catch( ExceptionName e2 ) {
  //catch block
}
catch( ExceptionName eN ) {
  //catch block
}
*try* ブロックが異なる状況で複数の例外を発生させた場合に、複数の *catch* ステートメントをリストして、異なるタイプの例外をキャッチできます。

Dでの例外のスロー

*throw* ステートメントを使用して、コードブロック内のどこにでも例外をスローできます。 throwステートメントのオペランドは例外のタイプを決定し、任意の式にすることができ、式の結果のタイプはスローされる例外のタイプを決定します。

次の例では、ゼロ除算条件が発生すると例外がスローされます-

double division(int a, int b) {
   if( b == 0 ) {
      throw new Exception("Division by zero condition!");
   }

   return (a/b);
}

Dでの例外のキャッチ

*try* ブロックに続く *catch* ブロックは、例外をキャッチします。 キャッチする例外のタイプを指定できます。これは、キーワードcatchに続く括弧内に表示される例外宣言によって決まります。
try {
  //protected code
}

catch( ExceptionName e ) {
  //code to handle ExceptionName exception
}

上記のコードは、 ExceptionName タイプの例外をキャッチします。 あなたがキャッチブロックがtryブロックでスローされる例外の任意のタイプを処理するように指定したい場合は、次のように例外宣言を囲む括弧の間に、省略記号を置く必要があります-

try {
  //protected code
}

catch(...) {
  //code to handle any exception
}

次の例は、ゼロ除算例外をスローします。 catchブロックでキャッチされます。

import std.stdio;
import std.string;

string division(int a, int b) {
   string result = "";

   try {
      if( b == 0 ) {
         throw new Exception("Cannot divide by zero!");
      } else {
         result = format("%s",a/b);
      }
   } catch (Exception e) {
      result = e.msg;
   }

   return result;
}

void main () {
   int x = 50;
   int y = 0;

   writeln(division(x, y));

   y = 10;
   writeln(division(x, y));
}

上記のコードがコンパイルおよび実行されると、前のセクションで作成されたファイルを読み取り、次の結果を生成します-

Cannot divide by zero!
5

D-契約プログラミング

Dプログラミングのコントラクトプログラミングは、エラー処理のシンプルでわかりやすい手段を提供することに焦点を当てています。 Dのコントラクトプログラミングは、3種類のコードブロックによって実装されます-

  • ボディブロック
  • ブロックで
  • アウトブロック

Dのボディブロック

本体ブロックには、実行の実際の機能コードが含まれています。 入出力ブロックはオプションですが、本体ブロックは必須です。 以下に簡単な構文を示します。

return_type function_name(function_params)
in {
  //in block
}

out (result) {
  //in block
}

body {
  //actual function block
}

Dの事前条件のブロック内

ブロック内は、入力パラメーターが受け入れ可能で、コードで処理できる範囲内にあるかどうかを検証する単純な事前条件用です。 ブロック内の利点は、すべての入力条件をまとめて、関数の実際の本体から分離できることです。 パスワードを最小長で検証するための簡単な前提条件を以下に示します。

import std.stdio;
import std.string;

bool isValid(string password)
in {
   assert(password.length>=5);
}

body {
  //other conditions
   return true;
}

void main() {
   writeln(isValid("password"));
}

上記のコードがコンパイルおよび実行されると、前のセクションで作成されたファイルを読み取り、次の結果を生成します-

true

Dの事後条件のアウトブロック

outブロックは、関数からの戻り値を処理します。 戻り値が期待される範囲内にあることを検証します。 月と年を組み合わせた10進数の年齢形式に変換する、入力と出力の両方を含む簡単な例を以下に示します。

import std.stdio;
import std.string;

double getAge(double months,double years)
in {
   assert(months >= 0);
   assert(months <= 12);
}

out (result) {
   assert(result>=years);
}

body {
   return years + months/12;
}

void main () {
   writeln(getAge(10,12));
}

上記のコードがコンパイルおよび実行されると、前のセクションで作成されたファイルを読み取り、次の結果を生成します-

12.8333

Dプログラミング-条件付きコンパイル

条件付きコンパイルは、CおよびC ++の#if/#else/#endifと同様に、コンパイルするコードとコンパイルしないコードを選択するプロセスです。 まだコンパイルされていないステートメントは、構文的に正しいものでなければなりません。

条件付きコンパイルには、コンパイル時に評価可能な条件チェックが含まれます。 if、for、whileなどのランタイム条件ステートメントは、条件付きコンパイル機能ではありません。 Dの次の機能は、条件付きコンパイル用です-

  • デバッグ
  • バージョン
  • 静的な場合

Dのデバッグステートメント

_debug_は、プログラム開発中に役立ちます。 デバッグとしてマークされた式とステートメントは、-debugコンパイラスイッチが有効になっている場合にのみプログラムにコンパイルされます。

debug a_conditionally_compiled_expression;

debug {
  //... conditionally compiled code ...
} else {
  //... code that is compiled otherwise ...
}

else節はオプションです。 上記の単一の式とコードブロックは、-debugコンパイラスイッチが有効になっている場合にのみコンパイルされます。

行を完全に削除する代わりに、代わりにデバッグとしてマークすることができます。

debug writefln("%s debug only statement", value);

このような行は、-debugコンパイラスイッチが有効な場合にのみプログラムに含まれます。

dmd test.d -oftest -w -debug

Dのデバッグ(タグ)ステートメント

デバッグステートメントには、プログラムに選択的に含める名前(タグ)を付けることができます。

debug(mytag) writefln("%s not found", value);

このような行は、-debugコンパイラスイッチが有効な場合にのみプログラムに含まれます。

dmd test.d -oftest -w -debug = mytag

デバッグブロックにもタグを含めることができます。

debug(mytag) {
  //
}

一度に複数のデバッグタグを有効にすることができます。

dmd test.d -oftest -w -debug = mytag1 -debug = mytag2

Dのデバッグ(レベル)ステートメント

デバッグレベルを数値レベルで関連付けるほうが便利な場合があります。 レベルを上げると、より詳細な情報が得られます。

import std.stdio;

void myFunction() {
   debug(1) writeln("debug1");
   debug(2) writeln("debug2");
}

void main() {
   myFunction();
}

指定されたレベル以下のデバッグ式とブロックがコンパイルされます。

$ dmd test.d -oftest -w -debug = 1
$ ./test
debug1

Dのバージョン(タグ)およびバージョン(レベル)ステートメント

バージョンはデバッグに似ており、同じ方法で使用されます。 else節はオプションです。 バージョンは基本的にデバッグと同じように機能しますが、個別のキーワードを使用すると、関連のない用途を区別できます。 デバッグと同様に、複数のバージョンを有効にできます。

import std.stdio;

void myFunction() {
   version(1) writeln("version1");
   version(2) writeln("version2");
}

void main() {
   myFunction();
}

指定されたレベル以下のデバッグ式とブロックがコンパイルされます。

$ dmd test.d -oftest -w -version = 1
$ ./test
version1

静的な場合

静的ifは、ifステートメントと同等のコンパイル時間です。 ifステートメントと同様に、静的ifは論理式を取り、それを評価します。 ifステートメントとは異なり、静的ifは実行フローに関するものではありません。むしろ、コードの一部をプログラムに含めるかどうかを決定します。

if式は、構文と意味の両方で、先ほど見たis演算子とは無関係です。 コンパイル時に評価されます。 0または1のint値を生成します。括弧で指定された式に依存します。 取る式は論理式ではありませんが、is式自体はコンパイル時の論理式として使用されます。 静的なif条件とテンプレート制約で特に役立ちます。

import std.stdio;

enum Days {
   sun,
   mon,
   tue,
   wed,
   thu,
   fri,
   sat
};

void myFunction(T)(T mytemplate) {
   static if (is (T == class)) {
      writeln("This is a class type");
   } else static if (is (T == enum)) {
      writeln("This is an enum type");
   }
}

void main() {
   Days day;
   myFunction(day);
}

コンパイルして実行すると、次のような出力が得られます。

This is an enum type

Dプログラミング-クラスとオブジェクト

クラスは、オブジェクト指向プログラミングをサポートするDプログラミングの中心的な機能であり、多くの場合、ユーザー定義型と呼ばれます。

クラスはオブジェクトの形式を指定するために使用され、データ表現とそのデータを1つのきれいなパッケージに操作するためのメソッドを組み合わせます。 クラス内のデータと関数は、クラスのメンバーと呼ばれます。

Dクラスの定義

クラスを定義するときには、データ型の設計図を定義します。 これは実際にはデータを定義しませんが、クラス名の意味、つまりクラスのオブジェクトが何を構成し、そのようなオブジェクトで実行できる操作を定義します。

クラス定義は、キーワード class で始まり、その後にクラス名が続きます。クラス本体は、中括弧のペアで囲まれています。 クラス定義の後には、セミコロンまたは宣言のリストを続ける必要があります。 たとえば、次のようにキーワード class を使用してBoxデータ型を定義しました-

class Box {
   public:
      double length;  //Length of a box
      double breadth; //Breadth of a box
      double height;  //Height of a box
}

キーワード public は、それに続くクラスのメンバーのアクセス属性を決定します。 パブリックメンバーは、クラスオブジェクトの範囲内であればどこからでもクラスの外部からアクセスできます。 また、サブセクションで説明する private または protected としてクラスのメンバーを指定することもできます。

Dオブジェクトの定義

クラスはオブジェクトの設計図を提供するため、基本的にオブジェクトはクラスから作成されます。 基本型の変数を宣言するのとまったく同じ種類の宣言を使用して、クラスのオブジェクトを宣言します。 次のステートメントは、クラスボックスの2つのオブジェクトを宣言します-

Box Box1;         //Declare Box1 of type Box
Box Box2;         //Declare Box2 of type Box

オブジェクトBox1とBox2の両方には、データメンバーの独自のコピーがあります。

データメンバーへのアクセス

クラスのオブジェクトのパブリックデータメンバーには、直接メンバーアクセス演算子(。)を使用してアクセスできます。 物事を明確にするために次の例を試してみましょう-

import std.stdio;

class Box {
   public:
      double length;  //Length of a box
      double breadth; //Breadth of a box
      double height;  //Height of a box
}

void main() {
   Box box1 = new Box();   //Declare Box1 of type Box
   Box box2 = new Box();   //Declare Box2 of type Box
   double volume = 0.0;    //Store the volume of a box here

  //box 1 specification
   box1.height = 5.0;
   box1.length = 6.0;
   box1.breadth = 7.0;

  //box 2 specification
   box2.height = 10.0;
   box2.length = 12.0;
   box2.breadth = 13.0;

  //volume of box 1
   volume = box1.height *box1.length* box1.breadth;
   writeln("Volume of Box1 : ",volume);

  //volume of box 2
   volume = box2.height *box2.length* box2.breadth;
   writeln("Volume of Box2 : ", volume);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Volume of Box1 : 210
Volume of Box2 : 1560

プライベートメンバーおよび保護されたメンバーには、直接メンバーアクセス演算子(。)を使用して直接アクセスできないことに注意することが重要です。 すぐに、プライベートおよび保護されたメンバーにアクセスする方法を学びます。

Dのクラスとオブジェクト

これまでのところ、Dクラスとオブジェクトに関する非常に基本的な考えを持っています。 Dクラスとオブジェクトに関連するさらに興味深い概念があります。これらについては、以下にリストするさまざまなサブセクションで説明します-

Sr.No. Concept & Description
1

Class member functions

クラスのメンバー関数は、他の変数と同様に、クラス定義内にその定義またはプロトタイプを持つ関数です。

2

Class access modifiers

クラスメンバーは、public、private、またはprotectedとして定義できます。 デフォルトでは、メンバーはプライベートと見なされます。

3

Constructor & destructor

クラスコンストラクターは、クラスの新しいオブジェクトが作成されたときに呼び出されるクラス内の特別な関数です。 デストラクタも、作成されたオブジェクトが削除されるときに呼び出される特別な関数です。

4

The this pointer in D

すべてのオブジェクトには、オブジェクト自体を指す特別なポインター this があります。

5

Pointer to D classes

クラスへのポインターは、構造体へのポインターとまったく同じ方法で実行されます。 実際、クラスは実際には機能を備えた単なる構造体です。

6

Static members of a class

クラスのデータメンバーと関数メンバーの両方を静的として宣言できます。

Dプログラミング-継承

オブジェクト指向プログラミングで最も重要な概念の1つは継承です。 継承により、別のクラスの観点からクラスを定義できるため、アプリケーションの作成と保守が容易になります。 これにより、コード機能を再利用して実装時間を短縮することもできます。

クラスを作成するとき、プログラマは完全に新しいデータメンバーとメンバー関数を記述する代わりに、新しいクラスが既存のクラスのメンバーを継承するように指定できます。 この既存のクラスは base クラスと呼ばれ、新しいクラスは derived クラスと呼ばれます。

継承の概念は、関係です。 たとえば、哺乳類IS-A動物、犬IS-A哺乳類、したがって犬IS-A動物などです。

Dの基本クラスと派生クラス

クラスは複数のクラスから派生できます。つまり、複数の基本クラスからデータと関数を継承できます。 派生クラスを定義するには、クラス派生リストを使用して基本クラスを指定します。 クラス派生リストは、1つまたは複数のベースクラスに名前を付け、形式があります-

class derived-class: base-class

次のように基本クラス*形状*とその派生クラス*長方形*を考えてください-

import std.stdio;

//Base class
class Shape {
   public:
      void setWidth(int w) {
         width = w;
      }

      void setHeight(int h) {
         height = h;
      }

   protected:
      int width;
      int height;
}

//Derived class
class Rectangle: Shape {
   public:
      int getArea() {
         return (width * height);
      }
}

void main() {
   Rectangle Rect = new Rectangle();

   Rect.setWidth(5);
   Rect.setHeight(7);

  //Print the area of the object.
   writeln("Total area: ", Rect.getArea());
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Total area: 35

アクセス制御と継承

派生クラスは、その基本クラスのすべての非プライベートメンバーにアクセスできます。 したがって、派生クラスのメンバー関数にアクセスできないはずの基本クラスメンバーは、基本クラスでprivateとして宣言する必要があります。

派生クラスは、次の例外を除いてすべての基本クラスのメソッドを継承します-

  • 基本クラスのコンストラクタ、デストラクタ、およびコピーコンストラクタ。 *基本クラスのオーバーロードされた演算子。

マルチレベル継承

継承は複数レベルにすることができ、次の例に示します。

import std.stdio;

//Base class
class Shape {
   public:
      void setWidth(int w) {
         width = w;
      }

      void setHeight(int h) {
         height = h;
      }

   protected:
      int width;
      int height;
}

//Derived class
class Rectangle: Shape {
   public:
      int getArea() {
         return (width* height);
      }
}

class Square: Rectangle {
   this(int side) {
      this.setWidth(side);
      this.setHeight(side);
   }
}

void main() {
   Square square = new Square(13);

  //Print the area of the object.
   writeln("Total area: ", square.getArea());
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Total area: 169

Dプログラミング-オーバーロード

Dを使用すると、同じスコープ内の*関数*名または*演算子*に対して複数の定義を指定できます。これらは、それぞれ*関数のオーバーロード*および*演算子のオーバーロード*と呼ばれます。

オーバーロードされた宣言は、同じスコープ内の前の宣言と同じ名前で宣言された宣言です。ただし、両方の宣言が異なる引数と明らかに異なる定義(実装)を持っている点が異なります。

オーバーロードされた function または operator を呼び出すと、コンパイラは、関数または演算子の呼び出しに使用した引数の型を定義で指定されたパラメーターの型と比較することにより、使用する最も適切な定義を決定します。 最も適切なオーバーロード関数または演算子を選択するプロセスは、*オーバーロード解決*と呼ばれます。

関数のオーバーロード

同じスコープ内で同じ関数名に複数の定義を設定できます。 関数の定義は、引数リスト内の引数のタイプまたは数、あるいはその両方によって互いに異なる必要があります。 戻り型のみが異なる関数宣言をオーバーロードすることはできません。

次の例では、同じ関数* print()*を使用して異なるデータ型を印刷します-

import std.stdio;
import std.string;

class printData {
   public:
      void print(int i) {
         writeln("Printing int: ",i);
      }

      void print(double f) {
         writeln("Printing float: ",f );
      }

      void print(string s) {
         writeln("Printing string: ",s);
      }
};

void main() {
   printData pd = new printData();

  //Call print to print integer
   pd.print(5);

  //Call print to print float
   pd.print(500.263);

  //Call print to print character
   pd.print("Hello D");
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Printing int: 5
Printing float: 500.263
Printing string: Hello D

オペレータの過負荷

Dで使用可能な組み込み演算子のほとんどを再定義またはオーバーロードできます。 したがって、プログラマはユーザー定義型の演算子も使用できます。

演算子は、文字列opの後にオーバーロードする演算子に基づいて、Add、Subなどを使用してオーバーロードできます。 以下に示すように、演算子+をオーバーロードして2つのボックスを追加できます。

Box opAdd(Box b) {
   Box box = new Box();
   box.length = this.length + b.length;
   box.breadth = this.breadth + b.breadth;
   box.height = this.height + b.height;
   return box;
}

次の例は、メンバー関数を使用した演算子のオーバーロードの概念を示しています。 ここでは、オブジェクトは引数として渡され、そのプロパティはこのオブジェクトを使用してアクセスされます。 この演算子を呼び出すオブジェクトは、以下で説明するように this 演算子を使用してアクセスできます-

import std.stdio;

class Box {
   public:
      double getVolume() {
         return length *breadth* height;
      }

      void setLength( double len ) {
         length = len;
      }

      void setBreadth( double bre ) {
         breadth = bre;
      }

      void setHeight( double hei ) {
         height = hei;
      }

      Box opAdd(Box b) {
         Box box = new Box();
         box.length = this.length + b.length;
         box.breadth = this.breadth + b.breadth;
         box.height = this.height + b.height;
         return box;
      }

   private:
      double length;     //Length of a box
      double breadth;    //Breadth of a box
      double height;     //Height of a box
};

//Main function for the program
void main( ) {
   Box box1 = new Box();   //Declare box1 of type Box
   Box box2 = new Box();   //Declare box2 of type Box
   Box box3 = new Box();   //Declare box3 of type Box
   double volume = 0.0;    //Store the volume of a box here

  //box 1 specification
   box1.setLength(6.0);
   box1.setBreadth(7.0);
   box1.setHeight(5.0);

  //box 2 specification
   box2.setLength(12.0);
   box2.setBreadth(13.0);
   box2.setHeight(10.0);

  //volume of box 1
   volume = box1.getVolume();
   writeln("Volume of Box1 : ", volume);

  //volume of box 2
   volume = box2.getVolume();
   writeln("Volume of Box2 : ", volume);

  //Add two object as follows:
   box3 = box1 + box2;

  //volume of box 3
   volume = box3.getVolume();
   writeln("Volume of Box3 : ", volume);
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400

演算子のオーバーロードの種類

基本的に、以下にリストする3つのタイプの演算子のオーバーロードがあります。

Sr.No. Overloading Types
1 Unary Operators Overloading
2 Binary Operators Overloading
3 Comparison Operators Overloading

Dプログラミング-カプセル化

すべてのDプログラムは、次の2つの基本的な要素で構成されています-

  • プログラム文(コード)-これは、アクションを実行するプログラムの一部であり、関数と呼ばれます。
  • プログラムデータ-プログラム機能の影響を受けたプログラムの情報です。

カプセル化はオブジェクト指向プログラミングの概念であり、データとデータを一緒に操作する機能を結び付け、外部の干渉や誤用から安全に保ちます。 データのカプセル化により、*データ隠蔽*という重要なOOPコンセプトが生まれました。

  • データのカプセル化*はデータをバンドルするメカニズムであり、それらを使用する機能と*データの抽象化*はインターフェースのみを公開し、実装の詳細をユーザーから隠すメカニズムです。

Dは、 classes と呼ばれるユーザー定義型の作成を通じて、カプセル化とデータ隠蔽のプロパティをサポートします。 クラスには、 private 、protected、および public メンバーを含めることができることをすでに検討しました。 デフォルトでは、クラスで定義されているすべてのアイテムはプライベートです。 たとえば-

class Box {
   public:
      double getVolume() {
         return length *breadth* height;
      }
   private:
      double length;     //Length of a box
      double breadth;    //Breadth of a box
      double height;     //Height of a box
};

変数の長さ、幅、高さは private です。 つまり、それらはBoxクラスの他のメンバーのみがアクセスでき、プログラムの他の部分はアクセスできません。 これは、カプセル化を実現する1つの方法です。

クラスの一部を public (つまり、プログラムの他の部分からアクセス可能)にするには、 public キーワードの後に​​宣言する必要があります。 public指定子の後に定義されたすべての変数または関数は、プログラム内の他のすべての関数からアクセスできます。

あるクラスを別のクラスのフレンドにすると、実装の詳細が明らかになり、カプセル化が減少します。 各クラスの詳細をできるだけ他のすべてのクラスから隠しておくことが理想的です。

Dでのデータのカプセル化

パブリックメンバーとプライベートメンバーを持つクラスを実装するDプログラムは、データのカプセル化とデータの抽象化の例です。 次の例を考慮してください-

import std.stdio;

class Adder {
   public:
     //constructor
      this(int i = 0) {
         total = i;
      }

     //interface to outside world
      void addNum(int number) {
         total += number;
      }

     //interface to outside world
      int getTotal() {
         return total;
      };

   private:
     //hidden data from outside world
      int total;
}

void main( ) {
   Adder a = new Adder();

   a.addNum(10);
   a.addNum(20);
   a.addNum(30);
   writeln("Total ",a.getTotal());
}

上記のコードをコンパイルして実行すると、次の結果が生成されます-

Total 60

上記のクラスは数値を加算し、合計を返します。 パブリックメンバー addNum および getTotal は外部世界へのインターフェイスであり、ユーザーはクラスを使用するためにそれらを知る必要があります。 プライベートメンバーの合計は、外部から隠されていますが、クラスが適切に動作するために必要です。

Dのクラス設計戦略

私たちのほとんどは、実際にクラスメンバーを公開する必要がない限り、デフォルトではクラスメンバーをプライベートにするという苦い経験を​​通じて学んでいます。 それはちょうど良い*カプセル化*です。

この知恵はデータメンバーに最も頻繁に適用されますが、仮想機能を含むすべてのメンバーに等しく適用されます。 D-programming-interfaces

Dプログラミング-抽象クラス

抽象化とは、OOPでクラスを抽象化する機能のことです。 抽象クラスは、インスタンス化できないクラスです。 クラスの他のすべての機能は引き続き存在し、そのフィールド、メソッド、およびコンストラクターはすべて同じ方法でアクセスされます。 抽象クラスのインスタンスを作成することはできません。

クラスが抽象クラスであり、インスタンス化できない場合、そのクラスはサブクラスでない限りあまり使用されません。 これは通常、設計段階で抽象クラスがどのように発生するかです。 親クラスには子クラスのコレクションの共通機能が含まれていますが、親クラス自体は抽象的すぎて単独では使用できません。

Dで抽象クラスを使用する

*abstract* キーワードを使用して、クラスの抽象を宣言します。 キーワードは、クラスキーワードの前のどこかのクラス宣言に表示されます。 以下に、抽象クラスを継承して使用する方法の例を示します。

import std.stdio;
import std.string;
import std.datetime;

abstract class Person {
   int birthYear, birthDay, birthMonth;
   string name;

   int getAge() {
      SysTime sysTime = Clock.currTime();
      return sysTime.year - birthYear;
   }
}

class Employee : Person {
   int empID;
}

void main() {
   Employee emp = new Employee();
   emp.empID = 101;
   emp.birthYear = 1980;
   emp.birthDay = 10;
   emp.birthMonth = 10;
   emp.name = "Emp1";

   writeln(emp.name);
   writeln(emp.getAge);
}

上記のプログラムをコンパイルして実行すると、次の出力が得られます。

Emp1
37

抽象関数

関数と同様に、クラスも抽象的です。 そのような関数の実装は、そのクラスでは指定されていませんが、抽象関数を持つクラスを継承するクラスで提供する必要があります。 上記の例は、抽象関数で更新されています。

import std.stdio;
import std.string;
import std.datetime;

abstract class Person {
   int birthYear, birthDay, birthMonth;
   string name;

   int getAge() {
      SysTime sysTime = Clock.currTime();
      return sysTime.year - birthYear;
   }
   abstract void print();
}
class Employee : Person {
   int empID;

   override void print() {
      writeln("The employee details are as follows:");
      writeln("Emp ID: ", this.empID);
      writeln("Emp Name: ", this.name);
      writeln("Age: ",this.getAge);
   }
}

void main() {
   Employee emp = new Employee();
   emp.empID = 101;
   emp.birthYear = 1980;
   emp.birthDay = 10;
   emp.birthMonth = 10;
   emp.name = "Emp1";
   emp.print();
}

上記のプログラムをコンパイルして実行すると、次の出力が得られます。

The employee details are as follows:
Emp ID: 101
Emp Name: Emp1
Age: 37