A Beginner's Guide to D/Printable version

From Wikibooks, open books for an open world
Jump to navigation Jump to search
Before We Begin...
  1. Short Introduction to D
  2. D Compiler
  3. Editors Supporting D

D is a language following in the footsteps of the C-language family. It has the same general syntax, so anyone familiar with C, C++, Java or C# should be able to easily pick up the language. You should see the language comparison page for more details.

Since this page is about a short introduction to D, why not start with the all-famous Hello World.

import std.stdio;
 
void main()
{
  writefln("Hello World!");
}

For the experienced C programmer, the import and writefln should look strange. We will discuss them in later chapters.

There are currently three versions of the D compiler available. Digital Mars provides the DMD compiler, while there is a front end for the GNU Compiler Collection available called GDC, and a front end for the LLVM compiler called ldc.

The DMD compiler supports Windows, Linux x86, and OS X, whereas GDC and LLVM support many more platforms.

At the time of writing, the homepage boasts support for:

  • Linux (tested on Fedora Core 5 x86 and PowerPC)
  • Mac OS X 10.3.x, 10.4.x, 10.5.x
  • FreeBSD 5.2.1
  • Cygwin
  • MinGW
  • AIX (tested on 5.1)

On January 2, 2007, DMD released version 1.0 of their compiler, and GDC released version 0.21, which is based on the specification for DMD 1.0.

On January 9, 2009, the ldc project released a D frontend for the LLVM compiler. It supports OS X, Windows and Linux.

On February 11, 2009, DMD released dmd 1.040 (GM) binaries for OS X 10.5, Windows x86 and Linux x86.

On February 14, 2009, DMD released dmd 2.025 (alpha) binaries for OS X 10.5, Windows x86 and Linux x86.

There is currently no finalized IDE tool ala Eclipse for D development. Vim and Emacs come bundled with D editing modes, and since D is syntactically very similar to the other C-style languages, any editor which supports those might rudimentally support D.

However, for an exhaustive list of D-supporting IDEs, please refer here.

The Basics

This chapter introduces some fundamentals of the D programming language. When you are finished with it, you will be able to do some very simple interaction with the user.

Table of Contents[edit | edit source]

  1. The Structure of D
  2. Start at the Beginning
  3. Introduction to Modules
  4. Types and Math
  5. Basic Output
  6. Basic Input


This section assumes that you have read an introduction to programming in general, and know what a program is, and what source code is.

Programming[edit | edit source]

Fundamentally, a computer program is a series of instructions for a computer to perform. This is true of everything from a text editor to a 3D game. At its lowest level (the Central Processing Unit), a computer has a set of instructions that it can perform. Although this set of instructions is a programming language, it has a number of problems that make it difficult to use for common programming. Because of this, there are programming languages which are more expressive, and give the programmer an easier means of writing programs, such as D. A compiler is a program that converts code written in such a programming language into the language a computer can use (called machine code).

Many programming languages, including D, are expressed as a series of instructions, to be performed in order. This concept is easy for both a computer and a human to grasp. In D, these instructions are called statements.

Statements[edit | edit source]

A statement is a (possibly very complicated) instruction to the computer. A single statement ends with a ';' (semicolon), and any number of statements can be put immediately after each other, so long as they each end with a ';' (semicolon). So, the series of statements 'a', 'b', 'c' is written as:

a;
b;
c;

This code will cause the program to perform hypothetical instructions 'a', 'b' and 'c', in that order.

There are many types of statements, each of which serves a different purpose.

Variables[edit | edit source]

It's very important for a program to be able to store information. In D (and most programming languages), a location to store information is called a variable. There are several types of variables, and which type should be used depends on what sort of information the variable is supposed to store.

To use a variable, you first have to declare it. Declaring a variable simply designates space to store it, and associates a name you supply with that space. A variable declaration is a statement, with the type and name of the variable being declared. All of the types of variables will be introduced in later sections; for the moment only two will be introduced:

Type What it is
int A number, such as 1, 2, 3, 0, -4, -3, etc. Cannot have a decimal, such as 1.1, 2.025.
float A number, such as 1.1, 2, 3.141, 0, -4.15, -3, etc.

To declare a variable named 'foo', of the type int, use the following statement:

int foo;

Of course, this can be changed for any type or name, with the general format:

type name;

Assignment[edit | edit source]

A variable isn't very useful if you can't store something there. Storing a value in a variable is called assigning the value to the variable. A variable can only have a value assigned to it after it has been declared, as mentioned above. The following statement will assign the value 5 to the variable 'foo':

foo = 5;

Any variable can be assigned any value like this, with the general format:

variable = value;

Math[edit | edit source]

Math in D is straightforward. + is used for addition, - is used for subtraction, / is used for division, and * is used for multiplication. To add two numbers, you simply put a + between them, like so:

2 + 2

The order of operations is the same as in math. That is, * and / are done before + and -. So, 2 + 2 * 2 is 6, not 8. You can change this by using ( and ) (parenthesis). Anything inside parenthesis will be done before anything outside of them; again, as in normal math. While 2 + 2 * 2 is 6, (2 + 2) * 2 is 8.

Math like this can be used most anywhere that a simple value could be used, including in assignment:

foo = 2 + 2;

The above assigns the value 4 to the variable 'foo'.

Furthermore, as in algebra, you can use variable names instead of numbers in math. If there is a variable 'bar', 'foo' can be assigned a value of 'bar's value plus two with:

foo = bar + 2;

The difference between the types mentioned above can make division produce unexpected results. If both numbers (operands) are ints, the resulting value will be an int as well. For example, if you write the following division:

1 / 2

the result will be an int. The result is rounded down, so the result is not 0.5, but 0!

To avoid this, you can force one value to be a float:

1 / 2.0

Since a decimal point is included, the 2.0 is interpreted as a float, so the whole operation is now calculated as float and results in 0.5.

Functions[edit | edit source]

Any non-trivial program should, like any other non-trivial task, be subdivided into small pieces.

Washing a car can be subdivided into first cleaning the hood, then washing the windows, cleaning the rims, etc. You may need different tools, techniques and cleansers for each sub-cleaning, and you may subdivide some of these tasks even further because they are too complicated. Fundamentally, computers are quite stupid: they must have all of this subdivision and instruction done by a human, for them. A human brain does this task subdivision nearly automatically for fairly complex tasks.

Technically, functions in D encapsulate a group of statements to perform a more abstract task than a single statement could do. One or more of these statements can in turn be function calls (use of a function).

Functions helps to organize a program. In the car-wash example, you can write one function for cleaning a car window, and while you write it you don't have to think about other parts of the car.

Later, when you call (use) it in another function to clean the whole car, you don't have to remember how the cleaning of the window works in detail, as the "clean window" function "knows" these details already.

The actual structure of functions and calls will be discussed in a later chapter.

Comments[edit | edit source]

[TODO: Tighten up this section] One last topic to discuss is comments. Comments are parts of your source code file that you tell the D compiler to ignore. They are useful because it lets you document your code in the code itself, or remove parts of your code for testing purposes (known as commenting out code). D has three basic types of comments: line comments, non-nesting block comments, and nesting comments.

Line comments are very simple. They are begun by two slashes (//) and ended at the end of a line. They are mainly used for documenting or commenting out single lines of code. Non-nesting block comments start with a slash and an asterisk (/*) and end with an asterisk and slash (*/). They can span multiple lines, but they do not nest, or allow multiple levels, which means that even if you have more than one /* comment opener, it only takes a single */ comment closer to close them all. These comments are mainly used for documenting entire sections of code. The last type of comments, nesting comments, work like block comments excep they do nest and are deliminated by a slash and plus-sign (/+) and plus-sign and slash (+/). The following example is designed to show off the features of comments, and should not be taken as an example of the best use of them.

number = 5; //Number is set to 5
//number = 6;  //We don't want number set to 6
/* The next section of code subtracts one from number and assigns that value to number2 */
number2 = number - 1;
//The following lines demonstrate nesting vs. non-nesting comments:
/+ This type of comment /+ Can /+ nest +/
... +/ still in comment +/
/* This type cannot /* nest 
 */ number = 7; //number is now set to 7


Where the Program Begins[edit | edit source]

There is a special location in code that is used as a starting point for all programs in D. This location is called the main function. The first code you want to run, the code that will start all the other code, is in this function.

Exactly how the main function works will be introduced in a later chapter, but for the moment you just need to know what the code for one style of main function:

void main()
{
    // place code here
}

Putting the Concepts Together[edit | edit source]

You now have enough knowledge to write a small, simple but complete program. Extending the "hello world" program introduced earlier, you can make a program start and store a number, by combining a main function with some simple variable code learned in the previous section. Here is a complete program, that will store the value 3 in the int variable 'a' and print it to console:

import std.stdio;

void main()
{
    int a;
    a = 3;
    writefln(a);
}


(Java programmers: Please read the last subsection of this chapter)

What are modules?[edit | edit source]

Technically, modules group things which belong together logically. A module can contain any number of definitions for functions, variables, and other D constructs which will be introduced later. These parts can then be easily accessed by other modules in D.

Each D program consists of at least one module which contains the main() function (see previous section).

Practically, in D a module is just a file with D source code (which has normally the file suffix ".d").

What are they good for?[edit | edit source]

Reusable Code[edit | edit source]

In programming, making code reusable is very important. Without reusable code, the process of writing even a fairly trivial program would be overwhelmed by the tasks of rewriting all of the basic code needed, such as communication with the user or network and mathematical functions.

Overview[edit | edit source]

Similar to functions, modules are another way to cut down a complex program into smaller parts which can be looked at separately.

Importing[edit | edit source]

In D, to access a module X from module Y, module X must be imported in module Y.

Importing a module is achieved through an import statement, consisting of the keyword import and the name of the module. For example, to import the module 'std.cstream' (which will be discussed later), the following statement is used:

import std.cstream;

Of course, this can be generalized to import any module:

import module name;

Everything defined in the imported module (e.g. functions, variables, etc.) become known to the importing module and can be used there.

Packages[edit | edit source]

A package is a group of modules and possibly (sub)packages. Practically it is just a directory containing module source files and other directories. In the example above, "std" is a package containing the module "cstream" (among others). When importing a module, the package(s) containing it must always be mentioned in the import statement, separated by dots from each other and from the actual module name (very similar to the path to a file).

Packages are usually used to group together modules that handle one or more commonly-performed tasks. This type of package is known as a library. Most libraries specialize in doing a single task, but some are (much) more general. The Phobos Runtime Library is a notable example of the latter category. It is the D language's standard library and comes standard with all D compilers as the std package. It handles basic features not built into the language itself, such as input and output, common math functions, and much more. Although D compilers are required to include Phobos (or a 100% compatible library), it is not a part of the language itself. For that reason this book will not cover Phobos in-depth beyond what is necessary.

Writing Modules[edit | edit source]

It is of course possible to write your own modules. In fact, any D program you write will be contained in a module. However, dividing your programs into multiple modules will not be introduced in this book until several more fundamentals are introduced.

Info for Java programmers[edit | edit source]

  • Modules can contain zero or more class definitions and can also contain other entities, like e.g. functions.
  • There is no relation between the module name and the name of contained entities.
  • Module names follow the general rules for identifiers, the filename suffix is ".d".
  • Module can be organized in packages (using directories) as in Java.

What are types?[edit | edit source]

If you look directly at the contents of a computer's memory, you see only a lot of bytes. These are often shown as numbers but in fact they don't have a meaning on its own, not even the meaning of a number.

Types are used in computer languages like D or C to give one byte or a sequence of bytes a meaning. Types define if these bytes are a number, a text, a date or whatever.

The Basic Types[edit | edit source]

Two basic types have already been introduced: int and float, for non-decimal and decimal numbers, respectively. Here, all of the basic types are introduced, including limits on their values and how large they are in memory.

Type What it Is Lower Limit Upper Limit Storage Size
byte A very small, integer number. -128 127 8 bits (1 byte)
ubyte A very small, integer, non-negative number. 0 255 8 bits (1 byte)
short A small, integer number. -32768 32767 16 bits (2 bytes)
ushort A small, integer, non-negative number. 0 65535 16 bits (2 bytes)
int A integer number. -2147483648 2147483647 32 bits (4 bytes)
uint A integer, non-negative number. 0 4294967296 32 bits (4 bytes)
long A potentially huge, integer number. -9223372036854775808 9223372036854775807 64 bits (8 bytes)
ulong A potentially huge, integer, non-negative number. 0 18446744073709551616 64 bits (8 bytes)
float A number. About -3.4e38 About 3.4e38 32 bits (4 bytes)
ifloat An imaginary number. About -3.4e38i About 3.4e38i 32 bits (4 bytes)
double A potentially huge number. About -1.8e308 About 1.8e308 64 bits (8 bytes)
idouble A potentially huge number imaginary number. About -1.8e308i About 1.8e308i 64 bits (8 bytes)
real A potentially gigantic number. The maximum size of decimal number that the computer is capable of supporting on the processor. Only generally defined as larger than double on x86 processors. (On x86) About -5.9e4436 (On x86) About 5.9e4436 (On x86) 80 bits (10 bytes)
ireal A potentially gigantic imaginary number. (On x86) About -5.9e4436 (On x86) About 5.9e4436 (On x86) 80 bits (10 bytes)
char A single element of text. Usually used for displayable, printable text, but can technically be used for anything that will fit. 'a', '0', '.', '+', ' ' are all valid chars. There are several ways to encode a character to a char, this type uses the UTF-8 encoding that encodes the most used characters with a smaller size. 0 255 8 bits (1 byte)
wchar A single element of text encoded as UTF-16. If you are not sure what this means use 'char'. 0 65535 16 bits (2 byte)
dchar A single element of text encoded as UTF-32. If you are not sure what this means use 'char'. 0 (not a displayable text element) 4294967296 32 bits (4 byte)

Notes[edit | edit source]

The types which cannot be decimal take less time (are more efficient) for math. The numbers which can be decimal are unpredictably less accurate for larger numbers. chars can, in general, be used exactly like ubytes, but their purpose is different.

Initialization[edit | edit source]

[TODO: Should some of the following sub-sections be placed elsewhere?] You can initialize a basic variable, or set it to a default value, by assigning it a value in its declaration:

int five = 5;
double twoPointFive = 2.5;

In D all basic variables have a default value that they are initialized to if the programmer does not specify one. This may seem intuitive, but some languages (most notably C) do not automatically initialize variables unless explicitly told to do so which can lead to many problems because without initialization variables contain garbage by default (as an artifact of how computer memory works). All integer number types are default initialized to 0. All decimal number types are default initialized to NaN, a special value meaning "Not a Number". Character types are default initialized to their maximum values. If you specifically want any basic variable to be initialized with garbage, initialize them with the special value void which means no initialization.

int garbage = void; //This variable contains garbage

Note: Do not count on the garbage in a void-initialized variable being random; more often than not it will be anything but.

Imaginary and Complex Numbers[edit | edit source]

If you are not familiar with the mathematical concept of imaginary and complex numbers, feel free to skip this sub-section. D has built-in support for imaginary and complex decimal number types. D's imaginary and complex number types are based on its real decimal types (float, double, or real). An arithmatic operation involving a real decimal number and an imaginary or complex number results in a complex number and all complex arithmatic works the same as in algebra. The type names of imaginary number types are the same as real decimal types except they are prefixed with an i, and the type names of complex numbers are likewise the same as real decimal types except prefixed with a c. Imaginary numbers are initialized to NaN * 1.0i, and complex numbers are initialized to NaN + NaN * 1.0i. Imaginary literals are the same as decimal literals except they are followed by an i. Complex literals are simply real literals plus imaginary literals. For example:

 double realDouble = 5.0;
 idouble imaginaryDouble = 5.0i;
 cdouble complexDouble = 5.0 + 5.0i;
 cdouble exampleOperation = complexDouble + 5.0i; // exampleOperation now equals 5+10i

One footnote is that complex decimal types take up double the amount of memory as their base real and imaginary decimal types because complex types are really just a real decimal and an imaginary decimal number stored together as one variable.

User Defined Types[edit | edit source]

D also supports several user defined types including enums, structs, classes, and more. These types are very powerful, but they are much more complicated to use. They will be covered in-depth later in the book.

(Stub)

Arrays[edit | edit source]

An array is a way to group data of the same type. An array contains any number of elements, and each element is a value of a specified type.

Arrays can be made from any type (including other array types) similarly. The type for an array of ints with five elements is:

int[5]

So, a variable called 'a' which is an array of five ints is:

int[5] a;

Of course, this can be generalized for any type and count:

type[count]

You can also create arrays with no specified number of elements, which can then be expanded to fit any number of elements. These are called dynamic arrays, and the alternatives (with a set number of elements) are called static arrays. They are specified similarly to static arrays, but the count is excluded. A dynamic array of any number of ints is:

int[]

A variable called 'a' which is a dynamic array of any number of ints is:

int[] a;

And of course, this can be generalized for any type with:

type[]

[TODO: Array literals and initializing dynamic arrays to them? Should this be saved for later?]

Array Access[edit | edit source]

Array elements can be accessed like normal variables in code. That is, you can use an array element nearly anywhere you could use a simple value. To access element 1 from an array named 'a', for example:

a[1]

The number for the element is called the index. The range of valid indexes is from 0 to 1 less than the number of elements; that is, for an array with 5 elements, the range of valid indexes is from 0 to 4, not from 1 to 5.

Array access can of course be generalized:

array[index]

And the index can be far more complex than just a number: it can be a variable, or even a mathematical expression.

Strings[edit | edit source]

In D and many other language, a series of textual characters is called a string. In some languages, there is a special type dedicated to this, but D takes it at face value: a string is just a series of characters, and a series of characters is just an array of characters. So, you can use the char[] type (that is, a dynamic array of chars) as a string.

Similar to numeric types where you could either write a variable of numeric type or directly a fixed number in an arithmetic expression in the source code and where you could assign a fixed number to a numeric variable, you can write a fixed string in the source and assign it to a char[] variable (often also called string variable).

To mark something as a fixed string (a so-called string literal) it must be enclosed in double quotation marks:

"Hello, world!"

A program writing this string literal to a console window (also known as a DOS-box in Windows), such as the example given in Basic Output, would give:

Hello, world!

Notice the lack of quotes in the output.

Escape sequences[edit | edit source]

Sometimes you want to add "special" characters to a string literal. This means

  1. characters not available on your keyboard
  2. control characters which are not visible when printed but instead are a simple command to the console to do something.
  3. characters with particular meaning like the double quotation mark.

The most commonly used of the second category is the newline character which ends one line on a console
and starts a new one.

To include such characters in a string literal, you use escape sequences. An escape sequence starts with a backslash "\" in the literal followed by one or more characters describing the desired character. For a newline character simply add "n" after the backslash.

Example:

"Hello,\nworld!"

If you were to replace the above "Hello, world!" literal with this one, the program would write

Hello,
world!

on the console.

Other useful escape sequences are \" which adds a double quotation mark (without the backslash, the quotation mark would terminate the string literal) and \\ to add one backslash to the literal.

Example:

"\"Interesting, \\\nor not?\", he said"

Output:

"Interesting, \
or not?", he said

Substrings[edit | edit source]

Sometimes you need to just grab part of a string. To do this use Array slice notation, which is the two '.'s between the indices you want to get the substring of lower bound inclusive and upper bound exclusive. Say we just wanted "Interesting" from the string seen above, we would do the following.

Example:

"\"Interesting, \\\nor not?\", he said" [ 1 .. 12 ]

Output:

Interesting


Clipboard

To do:
non-ascii characters like umlauts. How to output them on Windows? Create some "magic" function?


(Stub)

One of the most basic operations you can do with a program is to print out some text in a console window (sometimes also called DOS-box). Even if you later only use graphical environments to show nice windows on the screen, writing to a console is always helpful to tell the programmer what the program is currently doing so he/she can check if it does the right thing and can find bugs (mistakes in the source code) in the program.

Very versatile functions to write something to the console are writef and writefln.

First of all, these functions are placed in a separate module named "std.stdio", so you have to import it, as you have learned in Introduction to Modules.

Here is a very simple program that only writes a single string literal to the console:

import std.stdio;
 
void main()
{
  writefln("Hello, world!");
}

If you compile and run it, you will see the text

Hello, world!

on the screen.

As you have learned in Types and Math you can use escape sequences, also in writefln:

import std.stdio;
 
void main()
{
  writefln("Hello,\nworld!");
}

Output:

Hello,
world!

The difference between writef and writefln is only that the latter automatically appends a "\n" after the output. Everywhere where you use writefln you can instead use writef and append the newline yourself, this is just less practical.

import std.stdio;

void main()
{
  writef("Hello, this ");
  writef("is all");
  writefln("on one line");
  writefln("Another line");
}

Output:

Hello, this is allon one line
Another line

There was no space after the "all" in the source, therefore there isn't a space in the output either.

It is a good idea to always have a newline after the last output of a program.

Printing Variables[edit | edit source]

writef() and writefln() are useful for more than just printing string literals. They can print any combination of variables with one of the basic numeric types or string types or arrays of such items.

import std.stdio;

void main()
{
  int i = 5;
  int[] ia = [1,2,4,3];
  double d = 3.1415;

  writefln(i + 3);
  writefln(ia);
  writefln(d);
  writefln("Integer ", i, " plus double ", d, " gives ", i + d);
}

Output:

8
[1,2,4,3]
3.1415
Integer 5 plus double 3.1415 gives 8.1415

As you can see in the last output of the example, you can also write multiple expressions as a comma-separated list of parameters to writef() and writefln().

This last line can also be written in another way with a format string which is more common to C programmers. A format string is a single string containing some fixed text and placeholders for data which is contained in variables or expressions. The values which must be placed in the format string are appended as additional parameters after the format string itself.

A placeholder in the format string begins always with a percent sign % and ends with a letter which defines how to output the value (the format character). For some letters there can be additional characters between percent sign and letter to control the output more precisely (especially for numbers).

The default all-purpose format character is the 's' which can be used everywhere. So the line above can be rewritten as

   writefln("Integer %s plus double %s gives %s", i, d, i + d);

with exactly the same output.

[TODO: More details] (stub)

The built-in capabilities of D (meaning the capabilities of the runtime library Phobos) for input from a console (alias terminal or DOS-Box) are very restricted.

Here is a simple program that demonstrates string input with din.readLine() which reads a complete line of input. A line means all characters typed until the user pressed RETURN on keyboard. It doesn't mean a physical line in the console-window which is usually 80 characters wide.

import std.cstream; /* for din.readLine() */
import std.stdio;   /* for writef()/writefln() */

void main() 
{
   char[] name;

   writef("Hello friend. Please enter your name: ");
   name = din.readLine();

   writefln("Thanks, %s", name);
}
Conditions and Loops


This chapter introduces the basics of conditions and loops, the basic constructs of program flow.

Table of Contents[edit | edit source]

  1. The Concepts of Conditionals and Loops
  2. Simple Branching
  3. Simple Iteration (The foreach loop)
  4. Simple Looping (The while and do-while loops)
  5. Complex Iteration: The for Loop
  6. Switch Statement (The Switch conditional)


At this point, you should know how to do some simple interaction with the user. But without conditions or loops, your programs can only follow one simple pattern.

One fundamental capability of any programming language is conditions. A condition statement, such as an if statement, causes different code to run depending on whether a condition is true or false. if statements will be introduced in the following section.

Another important ability is a loop. It's often necessary to make one section of code run multiple times. The foreach, for and while loops allow this. They will be introduced later in this chapter.


The if Conditional[edit | edit source]

Example: More Complicated Input - a program that asks age and makes a "clever" response

import std.c.stdio;
import std.stdio;

void main()
{
  // Create a variable that can hold an integer
  int age;
  
  // writef does not flush on all platforms. If you remove fflush
  // below, the text might be written after you enter your number.
  writef("Hello, how old are you? ");
  fflush(stdout);

  // scanf reads in a number. %d tells scanf it is a number and the number
  //   is placed in the variable 'age'.
  // The & sign in &age tells the compiler that it is a reference to the
  //   variable that should be sent. 
  scanf("%d",&age);

  // Print different messages depending on how old the user is.
  if(age < 16) 
  {
    // If age is below 16 you are not allowed to practice car-driving in Sweden
    writefln("You are not old enough to practice car driving!");
  }
  else if (age < 18) /* 16 <= age < 18 */
  {  
    writefln("You may practice if you have a permission, but not drive alone!");
  } 
  else /* You are at least 18 so everything should be alright */
  {
    writefln("You may drive as long as you have a licence!");
  }
}

Simple Iteration: The foreach loop[edit | edit source]

The foreach statement provides a number of ways to iterate through an array in either direction. It takes as its arguments the name of a variable that will contain an element in the array, and the array to be traversed. It is followed by the statements to be executed. It is possible to obtain the index used to find a specific position in the array. In the following example we initialize an array of integers and iterate through it using foreach and foreach_reverse. You do not need to specify a type for the value.

import std.stdio;
 
int main()
{
    int[] arr = [ 1, 3, 5 ];

    foreach (value; arr)
        writefln("foreach: value = %d", value);

    foreach (index, value; arr)
    {
        writefln("foreach: index = %d, value = %d", index, value);
    }

    foreach_reverse (index, value; arr)
    {
        writefln("foreach_reverse: index = %d, value = %d", index, value);
    }

    return 0;
}

As with the if statement in previous chapters, you do not need brackets if there is only one statement following foreach. It is worth noting the use of a comma instead of semi-colon between index and value.

The code prints out the following:

foreach: value = 1
foreach: value = 3
foreach: value = 5
foreach: index = 0, value = 1
foreach: index = 1, value = 3
foreach: index = 2, value = 5
foreach_reverse: index = 2, value = 5
foreach_reverse: index = 1, value = 3
foreach_reverse: index = 0, value = 1

Cycling using foreach is done on a copy of the values, meaning that changes to them are not reflected in the originals. If we want to change the original array's values, that can be done by adding the type modifiers out or ref (equal to inout). The first can only be written to, the second can be read and written. If you attempt to read an out variable, it will be initialized. Adding the type modifier to an array index results in a compiler error.

  int[] arr = [ 1, 3, 5 ];
 
  foreach (ref value; arr)
  {
      value++;
  }

  foreach (key, inout value; arr)
  {
      value += 10; // Adds 10 to value
  }

  foreach (key, value; arr)
  {
      writefln("arr[%d] = %d", key, value);
  }

Output:

arr[0] = 12
arr[1] = 14
arr[2] = 16


One of the simplest kinds of loops in D is the while loop. This loop will keep looping as long as its condition is true. This also makes the while loop very flexible—virtually any kind of loop can be written as a while loop, if you try hard enough. A while loop looks like this:

while(condition)
{
	//code goes here
}

The while loop will see if the condition is true, and if it is, it'll run the code inside it. It'll keep doing that until the condition is false.

As was shown in the last section, the foreach loop is useful for looping over the contents of some kind of container. But what about when you don't know how much data you have, such as when you're reading from a file? A while loop can help in this case.

import std.stdio;
import std.stream;

void main()
{
	char[][] names;
	File input = new File("names.txt");
	
	while(input.eof() == false)
		names ~= input.readLine();
		
	writefln("Read ", names.length, " names.");

	foreach(name; names)
		writefln(name);
}

So here we create an array of strings named names to hold a list of names we'll read in from a file. Then we create a File object. Remember in the Basic Input section, where we used din to read a line of text from the console? din is really a kind of File object, so we're doing the same thing here, except we're reading from a file on disk instead of from the user.

The important thing here is the while loop. Let's have a closer look at it:

while(input.eof() == false)
	names ~= input.readLine();

The condition is input.eof() == false. The .eof() method of File means "end of file." So we're going to use this loop to read data while we're not at the end of the file. In other words, we're going to read all the data out of the file. The body of the loop does just that—it reads a line from the file, and appends it to the end of our list of names.

At the end of our program, we write out the list of names, using the familiar foreach loop.

A second, very similar kind of loop is the do-while loop. In a while loop, the condition comes at the beginning of the loop, but in a do-while loop, the condition comes at the end. The reason this is significant is that with a while loop, if the condition is false the very first time, the loop body will never be run, so while loops can run their bodies 0 or more times. But with a do-while loop, the body is run at least once before the condition is checked. do-while loops look like this:

do
{
	code
} while(condition)

One case where this kind of loop comes up a lot is when you're trying to get valid input from the user. Usually you ask the user to enter something in, then you check it to see if it's valid, and if it's not, you keep asking them until they give you something good. This is what a do-while loop is for: it'll run the input code at least once, and keep running it until the condition is met.

import std.stdio;
import std.cstream;

void main()
{
	bool valid = false;
	bool yes;

	do
	{
		writef("Delete all your data?  y/n: ");

		char[] response = din.readLine();

		if(response.length > 0)
		{
			if(response[0] == 'y')
			{
				yes = true;
				valid = true;
			}
			else if(response[0] == 'n')
			{
				yes = false;
				valid = true;
			}
		}
	} while(valid == false)
}

This code uses the valid variable to keep track of whether what the user entered was valid or not. It'll be set to true once they do. Until then, though, the do-while loop will keep looping, asking for user input.

The most complicated loop statement that D provides is the for loop. The for loop is borrowed directly from C, and shares its syntax. It has the following form:

for(statements; conditional; statements) { body }

The first set of statements are separated by commas, and are executed before the loop begins. Variables can be declared in these statements. This is usually used to initialize an index variable. The conditional is a Boolean value, and is executed before each iteration of the loop. The body is only executed as long as the conditional remains true. The last set of statements are executed after the body, and usually increments an index variable.

The following is the typical use of the for loop:

import std.stdio;

void main()
{
   int arr[] = [1, 2, 3, 4, 5];
 
   for(size_t x=0; x<arr.sizeof; x++) {
      writefln("%d", arr[x]);
   }
}

However, more complicated for loops are possible. For example, a for loop can iterate over two arrays, in opposite directions, at the same time:

void reverse_array(out int[] dest, int[] src) {
    dest.length = src.length;

    for(size_t dx=0, sx=src.length-1; dx < dest.length; dx++, sx--) {
        dest[dx] = src[sx];
    }
}


Basics[edit | edit source]

The switch statement can be found in almost every programming language, it's commonly used for checking multiple conditions. Syntax is identical as it is in C++ or Java. It has following form:

switch(variable)
{
	case value_to_check:
		statements;
		break;
		
	default:
		statements;
}

Here's some simple example:

import std.stdio,
	std.string : strip;

void main()
{
	// Command that user want to launch
	string input;
	
	write("Enter some command: ");
	
	// Get input without whitespaces, such as newline
	input = strip(readln());
	
	// We are checking input variable
	switch( input )	
	{
		// If input is equals to '/hello'
		case "/hello":
			writeln("Hello!");
			break;
			
		// If it is equals to '/bye'
		case "/bye":
			writeln("Bye!");
			break;
			
		// None of specified, unknown command
		default:
			writeln("I don't know that command");
	}
}

And the result of our code:

Enter some command: /hello
Hello!

Note break keyword after each case! If it's bypassed each case after it will be called. So here's console output of code without breaks:

Enter some command: /hello
Hello!
Bye!
I don't know that command

As you may see, all cases after /hello were "called", this is useful if we want to call same statements in more that one case. Here's one more example:

string cmd = "/hi";

switch( cmd )
{
	case "/hi":
	case "/hello":
	case "/wassup":
		writeln("Hi!");
		break;
		
	// ...
}